Move to CSS modules
Also includes misc fixes * Use CSS modules for all the CSS. * Fix a bug in the filterbar which was not filtering anything. * Fix a l10n issue in songs view. * Update hook to clean build repo before running, preventing to push dev built code. * Update webpack build code.
This commit is contained in:
parent
921028c073
commit
2d9747482b
116
.bootstraprc
Normal file
116
.bootstraprc
Normal file
@ -0,0 +1,116 @@
|
||||
---
|
||||
# Output debugging info
|
||||
loglevel: disabled
|
||||
|
||||
# Major version of Bootstrap: 3 or 4
|
||||
bootstrapVersion: 3
|
||||
|
||||
# If Bootstrap version 3 is used - turn on/off custom icon font path
|
||||
useCustomIconFontPath: false
|
||||
|
||||
# Webpack loaders, order matters
|
||||
styleLoaders:
|
||||
- style
|
||||
- css
|
||||
- sass
|
||||
|
||||
# Extract styles to stand-alone css file
|
||||
# Different settings for different environments can be used,
|
||||
# It depends on value of NODE_ENV environment variable
|
||||
# This param can also be set in webpack config:
|
||||
# entry: 'bootstrap-loader/extractStyles'
|
||||
extractStyles: true
|
||||
# env:
|
||||
# development:
|
||||
# extractStyles: false
|
||||
# production:
|
||||
# extractStyles: true
|
||||
|
||||
|
||||
# Customize Bootstrap variables that get imported before the original Bootstrap variables.
|
||||
# Thus, derived Bootstrap variables can depend on values from here.
|
||||
# See the Bootstrap _variables.scss file for examples of derived Bootstrap variables.
|
||||
#
|
||||
# preBootstrapCustomizations: ./path/to/bootstrap/pre-customizations.scss
|
||||
|
||||
|
||||
# This gets loaded after bootstrap/variables is loaded
|
||||
# Thus, you may customize Bootstrap variables
|
||||
# based on the values established in the Bootstrap _variables.scss file
|
||||
#
|
||||
# bootstrapCustomizations: ./path/to/bootstrap/customizations.scss
|
||||
|
||||
|
||||
# Import your custom styles here
|
||||
# Usually this endpoint-file contains list of @imports of your application styles
|
||||
#
|
||||
# appStyles: ./path/to/your/app/styles/endpoint.scss
|
||||
|
||||
|
||||
### Bootstrap styles
|
||||
styles:
|
||||
|
||||
# Mixins
|
||||
mixins: true
|
||||
|
||||
# Reset and dependencies
|
||||
normalize: true
|
||||
print: true
|
||||
glyphicons: true
|
||||
|
||||
# Core CSS
|
||||
scaffolding: true
|
||||
type: true
|
||||
code: true
|
||||
grid: true
|
||||
tables: true
|
||||
forms: true
|
||||
buttons: true
|
||||
|
||||
# Components
|
||||
component-animations: true
|
||||
dropdowns: true
|
||||
button-groups: true
|
||||
input-groups: true
|
||||
navs: true
|
||||
navbar: true
|
||||
breadcrumbs: true
|
||||
pagination: true
|
||||
pager: true
|
||||
labels: true
|
||||
badges: true
|
||||
jumbotron: true
|
||||
thumbnails: true
|
||||
alerts: true
|
||||
progress-bars: true
|
||||
media: true
|
||||
list-group: true
|
||||
panels: true
|
||||
wells: true
|
||||
responsive-embed: true
|
||||
close: true
|
||||
|
||||
# Components w/ JavaScript
|
||||
modals: true
|
||||
tooltip: true
|
||||
popovers: true
|
||||
carousel: true
|
||||
|
||||
# Utility classes
|
||||
utilities: true
|
||||
responsive-utilities: true
|
||||
|
||||
### Bootstrap scripts
|
||||
scripts:
|
||||
transition: true
|
||||
alert: true
|
||||
button: true
|
||||
carousel: true
|
||||
collapse: true
|
||||
dropdown: true
|
||||
modal: true
|
||||
tooltip: true
|
||||
popover: true
|
||||
scrollspy: true
|
||||
tab: true
|
||||
affix: true
|
@ -21,13 +21,13 @@ A few `npm` scripts are provided:
|
||||
|
||||
Translations are handled by [react-intl](https://github.com/yahoo/react-intl/).
|
||||
|
||||
`npm run extractTranslations` output a file containing all the english
|
||||
`npm run --silent extractTranslations` output a file containing all the english
|
||||
translations, in the expected form. It is a mapping of ids and strings to
|
||||
translate, with an extra description provided as a comment at the end of the
|
||||
line, for some translation context.
|
||||
|
||||
Typically, if you want to translate to another `$LOCALE` (say `fr-FR`), create
|
||||
a folder `./app/locales/$LOCALE`, put inside the generated file from `npm run
|
||||
extractTranslations`, called `index.js`. Copy the lines in
|
||||
--silent extractTranslations`, called `index.js`. Copy the lines in
|
||||
`./app/locales/index.js` to include your new translation and translate all the
|
||||
strings in the `./app/locales/$LOCALE/index.js` file you have just created.
|
||||
|
7
TODO
7
TODO
@ -5,12 +5,8 @@
|
||||
8. Search
|
||||
9. Discover
|
||||
|
||||
|
||||
# CSS
|
||||
* Sidebar responsiveness
|
||||
* Move CSS in modules
|
||||
=> https://github.com/gajus/react-css-modules
|
||||
|
||||
* Focus stays on navbar
|
||||
|
||||
# API middleware
|
||||
* https://github.com/reactjs/redux/issues/1824#issuecomment-228609501
|
||||
@ -18,6 +14,7 @@
|
||||
* https://github.com/reactjs/redux/issues/644
|
||||
* https://github.com/peterpme/redux-crud-api-middleware/blob/master/README.md
|
||||
* https://github.com/madou/armory-front/tree/master/src/app/reducers
|
||||
* https://github.com/erikras/multireducer
|
||||
* Immutable.js (?) + get rid of lodash
|
||||
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
import React, { Component, PropTypes } from "react";
|
||||
import CSSModules from "react-css-modules";
|
||||
|
||||
import { formatLength } from "../utils";
|
||||
|
||||
import css from "../styles/Album.scss";
|
||||
|
||||
export class AlbumTrackRow extends Component {
|
||||
render () {
|
||||
const length = formatLength(this.props.track.length);
|
||||
@ -40,28 +43,34 @@ AlbumTracksTable.propTypes = {
|
||||
tracks: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
export class AlbumRow extends Component {
|
||||
class AlbumRowCSS extends Component {
|
||||
render () {
|
||||
return (
|
||||
<div className="row albumRow">
|
||||
<div className="col-sm-offset-2 col-xs-10 albumRowName">
|
||||
<div className="row" styleName="row">
|
||||
<div className="col-sm-offset-2 col-xs-10" styleName="nameRow">
|
||||
<h2>{this.props.album.name}</h2>
|
||||
</div>
|
||||
<div className="col-xs-2 albumRowArt">
|
||||
<p className="text-center"><img src={this.props.album.art} width="200" height="200" className="img-responsive img-circle art" alt={this.props.album.name} /></p>
|
||||
<div className="col-xs-2" styleName="artRow">
|
||||
<p className="text-center"><img src={this.props.album.art} width="200" height="200" className="img-responsive img-circle" styleName="art" alt={this.props.album.name} /></p>
|
||||
</div>
|
||||
<div className="col-sm-10 table-responsive">
|
||||
<AlbumTracksTable tracks={this.props.album.tracks} />
|
||||
{
|
||||
Array.isArray(this.props.album.tracks) ?
|
||||
<AlbumTracksTable tracks={this.props.album.tracks} /> :
|
||||
null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AlbumRow.propTypes = {
|
||||
AlbumRowCSS.propTypes = {
|
||||
album: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export let AlbumRow = CSSModules(AlbumRowCSS, css);
|
||||
|
||||
export default class Album extends Component {
|
||||
render () {
|
||||
return (
|
||||
|
@ -1,8 +1,11 @@
|
||||
import React, { Component, PropTypes } from "react";
|
||||
import CSSModules from "react-css-modules";
|
||||
|
||||
import { AlbumRow } from "./Album";
|
||||
|
||||
export default class Artist extends Component {
|
||||
import css from "../styles/Artist.scss";
|
||||
|
||||
class ArtistCSS extends Component {
|
||||
render () {
|
||||
var albumsRows = [];
|
||||
if (Array.isArray(this.props.artist.albums)) {
|
||||
@ -12,7 +15,7 @@ export default class Artist extends Component {
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div className="row artistNameRow">
|
||||
<div className="row" styleName="name">
|
||||
<div className="col-sm-12">
|
||||
<h1>{this.props.artist.name}</h1>
|
||||
<hr/>
|
||||
@ -23,7 +26,7 @@ export default class Artist extends Component {
|
||||
<p>{this.props.artist.summary}</p>
|
||||
</div>
|
||||
<div className="col-sm-3 text-center">
|
||||
<p><img src={this.props.artist.art} width="200" height="200" className="img-responsive img-circle art" alt={this.props.artist.name}/></p>
|
||||
<p><img src={this.props.artist.art} width="200" height="200" className="img-responsive img-circle" styleName="art" alt={this.props.artist.name}/></p>
|
||||
</div>
|
||||
</div>
|
||||
{ albumsRows }
|
||||
@ -32,6 +35,8 @@ export default class Artist extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
Artist.propTypes = {
|
||||
ArtistCSS.propTypes = {
|
||||
artist: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default CSSModules(ArtistCSS, css);
|
||||
|
@ -1,12 +1,15 @@
|
||||
import React, { Component, PropTypes } from "react";
|
||||
import CSSModules from "react-css-modules";
|
||||
import { defineMessages, injectIntl, intlShape, FormattedMessage } from "react-intl";
|
||||
|
||||
import { messagesMap } from "../utils";
|
||||
import messages from "../locales/messagesDescriptors/Login";
|
||||
|
||||
import css from "../styles/Login.scss";
|
||||
|
||||
const loginMessages = defineMessages(messagesMap(messages));
|
||||
|
||||
class LoginFormIntl extends Component {
|
||||
class LoginFormCSSIntl extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
@ -61,8 +64,8 @@ class LoginFormIntl extends Component {
|
||||
{
|
||||
this.props.error ?
|
||||
<div className="row">
|
||||
<div className="alert alert-danger">
|
||||
<p id="loginFormError">
|
||||
<div className="alert alert-danger" id="loginFormError">
|
||||
<p>
|
||||
<span className="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> { this.props.error }
|
||||
</p>
|
||||
</div>
|
||||
@ -105,7 +108,7 @@ class LoginFormIntl extends Component {
|
||||
<FormattedMessage {...loginMessages["app.login.rememberMe"]} />
|
||||
</label>
|
||||
</div>
|
||||
<div className="col-sm-6 col-sm-12 submit text-right">
|
||||
<div className="col-sm-6 col-xs-12 text-right" styleName="submit">
|
||||
<input type="submit" className="btn btn-default" aria-label={formatMessage(loginMessages["app.login.signIn"])} defaultValue={formatMessage(loginMessages["app.login.signIn"])} disabled={this.props.isAuthenticating} />
|
||||
</div>
|
||||
</div>
|
||||
@ -119,7 +122,7 @@ class LoginFormIntl extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
LoginFormIntl.propTypes = {
|
||||
LoginFormCSSIntl.propTypes = {
|
||||
username: PropTypes.string,
|
||||
endpoint: PropTypes.string,
|
||||
rememberMe: PropTypes.bool,
|
||||
@ -130,10 +133,10 @@ LoginFormIntl.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export let LoginForm = injectIntl(LoginFormIntl);
|
||||
export let LoginForm = injectIntl(CSSModules(LoginFormCSSIntl, css));
|
||||
|
||||
|
||||
export default class Login extends Component {
|
||||
class Login extends Component {
|
||||
render () {
|
||||
const greeting = (
|
||||
<p>
|
||||
@ -141,8 +144,8 @@ export default class Login extends Component {
|
||||
</p>
|
||||
);
|
||||
return (
|
||||
<div className="login text-center container-fluid">
|
||||
<h1><img src="./app/assets/img/ampache-blue.png" alt="A"/>mpache</h1>
|
||||
<div className="text-center container-fluid">
|
||||
<h1><img styleName="titleImage" src="./app/assets/img/ampache-blue.png" alt="A"/>mpache</h1>
|
||||
<hr/>
|
||||
{(!this.props.error && !this.props.info) ? greeting : null}
|
||||
<div className="col-sm-9 col-sm-offset-2 col-md-6 col-md-offset-3">
|
||||
@ -162,3 +165,5 @@ Login.propTypes = {
|
||||
error: PropTypes.string,
|
||||
info: PropTypes.string
|
||||
};
|
||||
|
||||
export default CSSModules(Login, css);
|
||||
|
@ -65,13 +65,13 @@ export class SongsTable extends Component {
|
||||
<FormattedMessage {...songsMessages["app.songs.title"]} />
|
||||
</th>
|
||||
<th>
|
||||
<FormattedMessage {...songsMessages["app.common.artist"]} />
|
||||
<FormattedMessage {...songsMessages["app.common.artist"]} values={{itemCount: 1}} />
|
||||
</th>
|
||||
<th>
|
||||
<FormattedMessage {...songsMessages["app.common.album"]} />
|
||||
<FormattedMessage {...songsMessages["app.common.album"]} values={{itemCount: 1}} />
|
||||
</th>
|
||||
<th>
|
||||
<FormattedMessage {...songsMessages["app.common.genre"]} />
|
||||
<FormattedMessage {...songsMessages["app.songs.genre"]} />
|
||||
</th>
|
||||
<th>
|
||||
<FormattedMessage {...songsMessages["app.songs.length"]} />
|
||||
|
@ -1,12 +1,15 @@
|
||||
import React, { Component, PropTypes } from "react";
|
||||
import CSSModules from "react-css-modules";
|
||||
import { defineMessages, injectIntl, intlShape, FormattedMessage } from "react-intl";
|
||||
|
||||
import { messagesMap } from "../../utils";
|
||||
import messages from "../../locales/messagesDescriptors/elements/FilterBar";
|
||||
|
||||
import css from "../../styles/elements/FilterBar.scss";
|
||||
|
||||
const filterMessages = defineMessages(messagesMap(messages));
|
||||
|
||||
class FilterBarIntl extends Component {
|
||||
class FilterBarCSSIntl extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
@ -21,14 +24,14 @@ class FilterBarIntl extends Component {
|
||||
render () {
|
||||
const {formatMessage} = this.props.intl;
|
||||
return (
|
||||
<div className="filter">
|
||||
<p className="col-xs-12 col-sm-6 col-md-4 col-md-offset-1 filter-legend" id="filterInputDescription">
|
||||
<div styleName="filter">
|
||||
<p className="col-xs-12 col-sm-6 col-md-4 col-md-offset-1" styleName="legend" id="filterInputDescription">
|
||||
<FormattedMessage {...filterMessages["app.filter.whatAreWeListeningToToday"]} />
|
||||
</p>
|
||||
<div className="col-xs-12 col-sm-6 col-md-4 input-group">
|
||||
<form className="form-inline" onSubmit={this.handleChange} aria-describedby="filterInputDescription">
|
||||
<div className="form-group">
|
||||
<input type="text" className="form-control filter-input" placeholder={formatMessage(filterMessages["app.filter.filter"])} aria-label={formatMessage(filterMessages["app.filter.filter"])} value={this.props.filterText} onChange={this.handleChange} ref="filterTextInput" />
|
||||
<div className="form-group" styleName="form-group">
|
||||
<input type="text" className="form-control" placeholder={formatMessage(filterMessages["app.filter.filter"])} aria-label={formatMessage(filterMessages["app.filter.filter"])} value={this.props.filterText} onChange={this.handleChange} ref="filterTextInput" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -37,11 +40,10 @@ class FilterBarIntl extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
FilterBarIntl.propTypes = {
|
||||
FilterBarCSSIntl.propTypes = {
|
||||
onUserInput: PropTypes.func,
|
||||
filterText: PropTypes.string,
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
|
||||
export let FilterBar = injectIntl(FilterBarIntl);
|
||||
export default FilterBar;
|
||||
export default injectIntl(CSSModules(FilterBarCSSIntl, css));
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { Component, PropTypes } from "react";
|
||||
import { Link} from "react-router";
|
||||
import CSSModules from "react-css-modules";
|
||||
import imagesLoaded from "imagesloaded";
|
||||
import Isotope from "isotope-layout";
|
||||
import Fuse from "fuse.js";
|
||||
@ -8,7 +9,9 @@ import _ from "lodash";
|
||||
import FilterBar from "./FilterBar";
|
||||
import Pagination from "./Pagination";
|
||||
|
||||
export class GridItem extends Component {
|
||||
import css from "../../styles/elements/Grid.scss";
|
||||
|
||||
class GridItemCSS extends Component {
|
||||
render () {
|
||||
var nSubItems = this.props.item[this.props.subItemsType];
|
||||
if (Array.isArray(nSubItems)) {
|
||||
@ -27,10 +30,10 @@ export class GridItem extends Component {
|
||||
// TODO: i18n
|
||||
const title = "Go to " + this.props.itemsType.rstrip("s") + " page";
|
||||
return (
|
||||
<div className="grid-item col-xs-6 col-sm-3 placeholders" id={id}>
|
||||
<div className="grid-item-content placeholder text-center">
|
||||
<Link title={title} to={to}><img src={this.props.item.art} width="200" height="200" className="img-responsive img-circle art" alt={this.props.item.name}/></Link>
|
||||
<h4 className="name">{this.props.item.name}</h4>
|
||||
<div className="grid-item col-xs-6 col-sm-3" styleName="placeholders" id={id}>
|
||||
<div className="grid-item-content text-center">
|
||||
<Link title={title} to={to}><img src={this.props.item.art} width="200" height="200" className="img-responsive img-circle art" styleName="art" alt={this.props.item.name}/></Link>
|
||||
<h4 className="name" styleName="name">{this.props.item.name}</h4>
|
||||
<span className="sub-items text-muted"><span className="n-sub-items">{nSubItems}</span> <span className="sub-items-type">{subItemsLabel}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
@ -38,12 +41,14 @@ export class GridItem extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
GridItem.propTypes = {
|
||||
GridItemCSS.propTypes = {
|
||||
item: PropTypes.object.isRequired,
|
||||
itemsType: PropTypes.string.isRequired,
|
||||
subItemsType: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export let GridItem = CSSModules(GridItemCSS, css);
|
||||
|
||||
|
||||
const ISOTOPE_OPTIONS = { /** Default options for Isotope grid layout. */
|
||||
getSortData: {
|
||||
@ -93,9 +98,9 @@ export class Grid extends Component {
|
||||
|
||||
// Apply filter on grid
|
||||
this.iso.arrange({
|
||||
filter: function () {
|
||||
var name = $(this).find(".name").text();
|
||||
return result.find(function (item) { return item.item.name == name; });
|
||||
filter: function (item) {
|
||||
var name = $(item).find(".name").text();
|
||||
return result.find(function (i) { return i.item.name == name; });
|
||||
},
|
||||
transitionDuration: "0.4s",
|
||||
getSortData: {
|
||||
|
@ -1,14 +1,17 @@
|
||||
import React, { Component, PropTypes } from "react";
|
||||
import { Link, withRouter } from "react-router";
|
||||
import CSSModules from "react-css-modules";
|
||||
import { defineMessages, injectIntl, intlShape, FormattedMessage, FormattedHTMLMessage } from "react-intl";
|
||||
|
||||
import { messagesMap } from "../../utils";
|
||||
import commonMessages from "../../locales/messagesDescriptors/common";
|
||||
import messages from "../../locales/messagesDescriptors/elements/Pagination";
|
||||
|
||||
import css from "../../styles/elements/Pagination.scss";
|
||||
|
||||
const paginationMessages = defineMessages(messagesMap(Array.concat([], commonMessages, messages)));
|
||||
|
||||
export class PaginationIntl extends Component {
|
||||
class PaginationCSSIntl extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.buildLinkTo.bind(this);
|
||||
@ -134,8 +137,8 @@ export class PaginationIntl extends Component {
|
||||
if (pagesButton.length > 1) {
|
||||
return (
|
||||
<div>
|
||||
<nav className="pagination-nav" aria-label={formatMessage(paginationMessages["app.pagination.pageNavigation"])}>
|
||||
<ul className="pagination">
|
||||
<nav className="pagination-nav" styleName="nav" aria-label={formatMessage(paginationMessages["app.pagination.pageNavigation"])}>
|
||||
<ul className="pagination" styleName="pointer">
|
||||
{ pagesButton }
|
||||
</ul>
|
||||
</nav>
|
||||
@ -171,12 +174,11 @@ export class PaginationIntl extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
PaginationIntl.propTypes = {
|
||||
PaginationCSSIntl.propTypes = {
|
||||
currentPage: PropTypes.number.isRequired,
|
||||
location: PropTypes.object.isRequired,
|
||||
nPages: PropTypes.number.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export let Pagination = withRouter(injectIntl(PaginationIntl));
|
||||
export default Pagination;
|
||||
export default withRouter(injectIntl(CSSModules(PaginationCSSIntl, css)));
|
||||
|
@ -1,39 +1,42 @@
|
||||
import React, { Component, PropTypes } from "react";
|
||||
import { IndexLink, Link} from "react-router";
|
||||
import CSSModules from "react-css-modules";
|
||||
import { defineMessages, injectIntl, intlShape, FormattedMessage } from "react-intl";
|
||||
|
||||
import { messagesMap } from "../../utils";
|
||||
import commonMessages from "../../locales/messagesDescriptors/common";
|
||||
import messages from "../../locales/messagesDescriptors/layouts/Sidebar";
|
||||
|
||||
import css from "../../styles/layouts/Sidebar.scss";
|
||||
|
||||
const sidebarLayoutMessages = defineMessages(messagesMap(Array.concat([], commonMessages, messages)));
|
||||
|
||||
export default class SidebarLayoutIntl extends Component {
|
||||
class SidebarLayoutIntl extends Component {
|
||||
render () {
|
||||
const { formatMessage } = this.props.intl;
|
||||
const isActive = {
|
||||
discover: (this.props.location.pathname == "/discover") ? "active" : "",
|
||||
browse: (this.props.location.pathname == "/browse") ? "active" : "",
|
||||
artists: (this.props.location.pathname == "/artists") ? "active" : "",
|
||||
albums: (this.props.location.pathname == "/albums") ? "active" : "",
|
||||
songs: (this.props.location.pathname == "/songs") ? "active" : "",
|
||||
search: (this.props.location.pathname == "/search") ? "active" : ""
|
||||
discover: (this.props.location.pathname == "/discover") ? "active" : "link",
|
||||
browse: (this.props.location.pathname == "/browse") ? "active" : "link",
|
||||
artists: (this.props.location.pathname == "/artists") ? "active" : "link",
|
||||
albums: (this.props.location.pathname == "/albums") ? "active" : "link",
|
||||
songs: (this.props.location.pathname == "/songs") ? "active" : "link",
|
||||
search: (this.props.location.pathname == "/search") ? "active" : "link"
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<div className="col-sm-1 col-md-2 sidebar hidden-xs">
|
||||
<h1 className="text-center">
|
||||
<IndexLink to="/">
|
||||
<img alt="A" src="./app/assets/img/ampache-blue.png"/>
|
||||
<div className="col-sm-1 col-md-2 hidden-xs" styleName="sidebar">
|
||||
<h1 className="text-center" styleName="title">
|
||||
<IndexLink to="/" styleName="link">
|
||||
<img alt="A" src="./app/assets/img/ampache-blue.png" styleName="imgTitle" />
|
||||
<span className="hidden-sm">mpache</span>
|
||||
</IndexLink>
|
||||
</h1>
|
||||
<nav aria-label={formatMessage(sidebarLayoutMessages["app.sidebarLayout.mainNavigationMenu"])}>
|
||||
<div className="navbar text-center icon-navbar">
|
||||
<div className="navbar text-center" styleName="icon-navbar">
|
||||
<div className="container-fluid">
|
||||
<ul className="nav navbar-nav icon-navbar-nav">
|
||||
<ul className="nav navbar-nav" styleName="nav">
|
||||
<li>
|
||||
<Link to="/" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.home"])}>
|
||||
<Link to="/" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.home"])} styleName="link">
|
||||
<span className="glyphicon glyphicon-home" aria-hidden="true"></span>
|
||||
<span className="sr-only">
|
||||
<FormattedMessage {...sidebarLayoutMessages["app.sidebarLayout.home"]} />
|
||||
@ -41,7 +44,7 @@ export default class SidebarLayoutIntl extends Component {
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/settings" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.settings"])}>
|
||||
<Link to="/settings" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.settings"])} styleName="link">
|
||||
<span className="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||
<span className="sr-only">
|
||||
<FormattedMessage {...sidebarLayoutMessages["app.sidebarLayout.settings"]} />
|
||||
@ -49,7 +52,7 @@ export default class SidebarLayoutIntl extends Component {
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/logout" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.logout"])}>
|
||||
<Link to="/logout" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.logout"])} styleName="link">
|
||||
<span className="glyphicon glyphicon-off" aria-hidden="true"></span>
|
||||
<span className="sr-only">
|
||||
<FormattedMessage {...sidebarLayoutMessages["app.sidebarLayout.logout"]} />
|
||||
@ -59,9 +62,9 @@ export default class SidebarLayoutIntl extends Component {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<ul className="nav nav-sidebar">
|
||||
<ul className="nav" styleName="nav">
|
||||
<li>
|
||||
<Link to="/discover" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.discover"])} className={isActive.discover}>
|
||||
<Link to="/discover" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.discover"])} styleName={isActive.discover}>
|
||||
<span className="glyphicon glyphicon-globe" aria-hidden="true"></span>
|
||||
<span className="hidden-sm">
|
||||
<FormattedMessage {...sidebarLayoutMessages["app.sidebarLayout.discover"]} />
|
||||
@ -69,7 +72,7 @@ export default class SidebarLayoutIntl extends Component {
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/browse" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.browse"])} className={isActive.browse}>
|
||||
<Link to="/browse" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.browse"])} styleName={isActive.browse}>
|
||||
<span className="glyphicon glyphicon-headphones" aria-hidden="true"></span>
|
||||
<span className="hidden-sm">
|
||||
<FormattedMessage {...sidebarLayoutMessages["app.sidebarLayout.browse"]} />
|
||||
@ -77,7 +80,7 @@ export default class SidebarLayoutIntl extends Component {
|
||||
</Link>
|
||||
<ul className="nav nav-list text-center">
|
||||
<li>
|
||||
<Link to="/artists" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.browseArtists"])} className={isActive.artists}>
|
||||
<Link to="/artists" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.browseArtists"])} styleName={isActive.artists}>
|
||||
<span className="glyphicon glyphicon-user" aria-hidden="true"></span>
|
||||
<span className="sr-only">
|
||||
<FormattedMessage {...sidebarLayoutMessages["app.common.artist"]} values={{itemCount: 42}} />
|
||||
@ -88,7 +91,7 @@ export default class SidebarLayoutIntl extends Component {
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/albums" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.browseAlbums"])} className={isActive.albums}>
|
||||
<Link to="/albums" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.browseAlbums"])} styleName={isActive.albums}>
|
||||
<span className="glyphicon glyphicon-cd" aria-hidden="true"></span>
|
||||
<span className="sr-only"><FormattedMessage {...sidebarLayoutMessages["app.common.album"]} values={{itemCount: 42}} /></span>
|
||||
<span className="hidden-sm">
|
||||
@ -97,7 +100,7 @@ export default class SidebarLayoutIntl extends Component {
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/songs" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.browseSongs"])} className={isActive.songs}>
|
||||
<Link to="/songs" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.browseSongs"])} styleName={isActive.songs}>
|
||||
<span className="glyphicon glyphicon-music" aria-hidden="true"></span>
|
||||
<span className="sr-only">
|
||||
<FormattedMessage {...sidebarLayoutMessages["app.common.song"]} values={{itemCount: 42}} />
|
||||
@ -110,7 +113,7 @@ export default class SidebarLayoutIntl extends Component {
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/search" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.search"])} className={isActive.search}>
|
||||
<Link to="/search" title={formatMessage(sidebarLayoutMessages["app.sidebarLayout.search"])} styleName={isActive.search}>
|
||||
<span className="glyphicon glyphicon-search" aria-hidden="true"></span>
|
||||
<span className="hidden-sm">
|
||||
<FormattedMessage {...sidebarLayoutMessages["app.sidebarLayout.search"]} />
|
||||
@ -121,7 +124,7 @@ export default class SidebarLayoutIntl extends Component {
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div className="col-sm-11 col-sm-offset-1 col-md-10 col-md-offset-2 main-panel">
|
||||
<div className="col-sm-11 col-sm-offset-1 col-md-10 col-md-offset-2" styleName="main-panel">
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
@ -135,5 +138,4 @@ SidebarLayoutIntl.propTypes = {
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
|
||||
export let SidebarLayout = injectIntl(SidebarLayoutIntl);
|
||||
export default SidebarLayout;
|
||||
export default injectIntl(CSSModules(SidebarLayoutIntl, css));
|
||||
|
6
app/dist/1.1.js
vendored
6
app/dist/1.1.js
vendored
File diff suppressed because one or more lines are too long
2
app/dist/1.1.js.map
vendored
2
app/dist/1.1.js.map
vendored
File diff suppressed because one or more lines are too long
38
app/dist/3.3.js
vendored
38
app/dist/3.3.js
vendored
File diff suppressed because one or more lines are too long
2
app/dist/fix.ie9.js
vendored
2
app/dist/fix.ie9.js
vendored
@ -1,2 +1,2 @@
|
||||
!function(e){function t(r){if(n[r])return n[r].exports;var a=n[r]={exports:{},id:r,loaded:!1};return e[r].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n={};return t.m=e,t.c=n,t.p="./",t(0)}({0:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(526);Object.keys(r).forEach(function(e){"default"!==e&&Object.defineProperty(t,e,{enumerable:!0,get:function(){return r[e]}})})},526:function(e,t){!function(t,n){function r(e,t){var n=e.createElement("p"),r=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x<style>"+t+"</style>",r.insertBefore(n.lastChild,r.firstChild)}function a(){var e=b.elements;return"string"==typeof e?e.split(" "):e}function o(e,t){var n=b.elements;"string"!=typeof n&&(n=n.join(" ")),"string"!=typeof e&&(e=e.join(" ")),b.elements=n+" "+e,s(t)}function c(e){var t=E[e[v]];return t||(t={},y++,e[v]=y,E[y]=t),t}function i(e,t,r){if(t||(t=n),f)return t.createElement(e);r||(r=c(t));var a;return a=r.cache[e]?r.cache[e].cloneNode():g.test(e)?(r.cache[e]=r.createElem(e)).cloneNode():r.createElem(e),!a.canHaveChildren||p.test(e)||a.tagUrn?a:r.frag.appendChild(a)}function l(e,t){if(e||(e=n),f)return e.createDocumentFragment();t=t||c(e);for(var r=t.frag.cloneNode(),o=0,i=a(),l=i.length;o<l;o++)r.createElement(i[o]);return r}function u(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return b.shivMethods?i(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+a().join().replace(/[\w\-:]+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(b,t.frag)}function s(e){e||(e=n);var t=c(e);return!b.shivCSS||d||t.hasCSS||(t.hasCSS=!!r(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),f||u(e,t),e}var d,f,m="3.7.3-pre",h=t.html5||{},p=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,g=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",y=0,E={};!function(){try{var e=n.createElement("a");e.innerHTML="<xyz></xyz>",d="hidden"in e,f=1==e.childNodes.length||function(){n.createElement("a");var e=n.createDocumentFragment();return"undefined"==typeof e.cloneNode||"undefined"==typeof e.createDocumentFragment||"undefined"==typeof e.createElement}()}catch(t){d=!0,f=!0}}();var b={elements:h.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:h.shivCSS!==!1,supportsUnknownElements:f,shivMethods:h.shivMethods!==!1,type:"default",shivDocument:s,createElement:i,createDocumentFragment:l,addElements:o};t.html5=b,s(n),"object"==typeof e&&e.exports&&(e.exports=b)}("undefined"!=typeof window?window:this,document)}});
|
||||
!function(e){function t(r){if(n[r])return n[r].exports;var a=n[r]={exports:{},id:r,loaded:!1};return e[r].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n={};return t.m=e,t.c=n,t.p="./",t(0)}({0:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(585);Object.keys(r).forEach(function(e){"default"!==e&&Object.defineProperty(t,e,{enumerable:!0,get:function(){return r[e]}})})},585:function(e,t){!function(t,n){function r(e,t){var n=e.createElement("p"),r=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x<style>"+t+"</style>",r.insertBefore(n.lastChild,r.firstChild)}function a(){var e=b.elements;return"string"==typeof e?e.split(" "):e}function o(e,t){var n=b.elements;"string"!=typeof n&&(n=n.join(" ")),"string"!=typeof e&&(e=e.join(" ")),b.elements=n+" "+e,s(t)}function c(e){var t=E[e[v]];return t||(t={},y++,e[v]=y,E[y]=t),t}function i(e,t,r){if(t||(t=n),f)return t.createElement(e);r||(r=c(t));var a;return a=r.cache[e]?r.cache[e].cloneNode():g.test(e)?(r.cache[e]=r.createElem(e)).cloneNode():r.createElem(e),!a.canHaveChildren||p.test(e)||a.tagUrn?a:r.frag.appendChild(a)}function l(e,t){if(e||(e=n),f)return e.createDocumentFragment();t=t||c(e);for(var r=t.frag.cloneNode(),o=0,i=a(),l=i.length;o<l;o++)r.createElement(i[o]);return r}function u(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return b.shivMethods?i(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+a().join().replace(/[\w\-:]+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(b,t.frag)}function s(e){e||(e=n);var t=c(e);return!b.shivCSS||d||t.hasCSS||(t.hasCSS=!!r(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),f||u(e,t),e}var d,f,m="3.7.3-pre",h=t.html5||{},p=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,g=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",y=0,E={};!function(){try{var e=n.createElement("a");e.innerHTML="<xyz></xyz>",d="hidden"in e,f=1==e.childNodes.length||function(){n.createElement("a");var e=n.createDocumentFragment();return"undefined"==typeof e.cloneNode||"undefined"==typeof e.createDocumentFragment||"undefined"==typeof e.createElement}()}catch(t){d=!0,f=!0}}();var b={elements:h.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:h.shivCSS!==!1,supportsUnknownElements:f,shivMethods:h.shivMethods!==!1,type:"default",shivDocument:s,createElement:i,createDocumentFragment:l,addElements:o};t.html5=b,s(n),"object"==typeof e&&e.exports&&(e.exports=b)}("undefined"!=typeof window?window:this,document)}});
|
||||
//# sourceMappingURL=fix.ie9.js.map
|
2
app/dist/fix.ie9.js.map
vendored
2
app/dist/fix.ie9.js.map
vendored
File diff suppressed because one or more lines are too long
47
app/dist/index.js
vendored
47
app/dist/index.js
vendored
File diff suppressed because one or more lines are too long
2
app/dist/index.js.map
vendored
2
app/dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
1
app/dist/reactIntlMessages.json
vendored
1
app/dist/reactIntlMessages.json
vendored
@ -1 +0,0 @@
|
||||
[]
|
10
app/dist/style.css
vendored
10
app/dist/style.css
vendored
File diff suppressed because one or more lines are too long
27
app/styles/Album.scss
Normal file
27
app/styles/Album.scss
Normal file
@ -0,0 +1,27 @@
|
||||
.row {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.art {
|
||||
composes: art from "./elements/Grid.scss";
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.nameRow h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.artRow p,
|
||||
.artRow img {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.nameRow,
|
||||
.artRow {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
13
app/styles/Artist.scss
Normal file
13
app/styles/Artist.scss
Normal file
@ -0,0 +1,13 @@
|
||||
.name > h1 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.name > hr {
|
||||
margin-top: 0.5em; /* Default value. */
|
||||
}
|
||||
|
||||
.art {
|
||||
composes: art from "./elements/Grid.scss";
|
||||
}
|
||||
|
||||
/* TODO: Use table-condensed if xs screen */
|
9
app/styles/Login.scss
Normal file
9
app/styles/Login.scss
Normal file
@ -0,0 +1,9 @@
|
||||
.titleImage {
|
||||
height: 46px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.submit {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
@ -1,248 +0,0 @@
|
||||
/* Firefox hack for responsive table */
|
||||
@-moz-document url-prefix() {
|
||||
fieldset {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sidebar
|
||||
*/
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||
background-color: #333;
|
||||
color: white;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
|
||||
/* Sidebar elements */
|
||||
.sidebar a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.sidebar h1 {
|
||||
margin: 0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.sidebar h1 img {
|
||||
height: 46px;
|
||||
}
|
||||
|
||||
.sidebar h1 a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Sidebar navigation */
|
||||
.sidebar .navbar {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.nav-sidebar {
|
||||
margin-right: -20px;
|
||||
margin-bottom: 20px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
.nav-sidebar > li > a {
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.sidebar,
|
||||
.sidebar .navbar .container-fluid {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.sidebar .nav-list {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sidebar .nav-sidebar > li > a {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.sidebar .nav-list > li > a {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-sidebar .nav-sidebar {
|
||||
margin-bottom: 0; /* No margin bottom for nested nav-sidebar. */
|
||||
}
|
||||
|
||||
.nav-sidebar > .active > a:focus,
|
||||
.nav > li > a:focus {
|
||||
color: #fff;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.nav-sidebar > .active > a,
|
||||
.nav-sidebar > .active > a:hover,
|
||||
.nav > li > a:hover {
|
||||
color: #fff;
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
.nav > li > a.active {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
.icon-navbar {
|
||||
background-color: #555;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
.icon-navbar-nav {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
vertical-align: top;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main content
|
||||
*/
|
||||
.main-panel {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.main-panel {
|
||||
padding-right: 40px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Filtering field
|
||||
*/
|
||||
div.filter {
|
||||
margin-bottom: 34px;
|
||||
}
|
||||
|
||||
.filter-legend {
|
||||
text-align: right;
|
||||
line-height: 34px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.filter-legend {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 767px) {
|
||||
.filter .form-group {
|
||||
width: 75%;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Placeholder dashboard ideas
|
||||
*/
|
||||
.placeholders {
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.placeholders h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.placeholder img:hover {
|
||||
transform: scale(1.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pager
|
||||
*/
|
||||
.pagination-nav {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pagination > li > span {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login screen
|
||||
*/
|
||||
.login h1 img {
|
||||
height: 46px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.login .submit {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Misc
|
||||
*/
|
||||
|
||||
.art {
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
width: 75%;
|
||||
height: auto;
|
||||
|
||||
/* doiuse-disable viewport-units */
|
||||
|
||||
max-width: 25vw;
|
||||
max-height: 25vw;
|
||||
|
||||
/* doiuse-enable viewport-units */
|
||||
}
|
||||
|
||||
.albumRow {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.artistNameRow h1 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.artistNameRow hr {
|
||||
margin-top: 0.5em; /* Default value. */
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.table-responsive {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.albumRowName h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.albumRowArt p,
|
||||
.albumRowArt img {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.albumRowName,
|
||||
.albumRowArt {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Use table-condensed if xs screen */
|
7
app/styles/common/common.scss
Normal file
7
app/styles/common/common.scss
Normal file
@ -0,0 +1,7 @@
|
||||
:global {
|
||||
@media (max-width: 767px) {
|
||||
.table-responsive {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
8
app/styles/common/hacks.scss
Normal file
8
app/styles/common/hacks.scss
Normal file
@ -0,0 +1,8 @@
|
||||
:global {
|
||||
/* Firefox hack for responsive table */
|
||||
@-moz-document url-prefix() {
|
||||
fieldset {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
}
|
2
app/styles/common/index.js
Normal file
2
app/styles/common/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./hacks.scss";
|
||||
export * from "./common.scss";
|
20
app/styles/elements/FilterBar.scss
Normal file
20
app/styles/elements/FilterBar.scss
Normal file
@ -0,0 +1,20 @@
|
||||
.filter {
|
||||
margin-bottom: 34px;
|
||||
}
|
||||
|
||||
.legend {
|
||||
text-align: right;
|
||||
line-height: 34px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.legend {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 767px) {
|
||||
.form-group {
|
||||
width: 75%;
|
||||
}
|
||||
}
|
26
app/styles/elements/Grid.scss
Normal file
26
app/styles/elements/Grid.scss
Normal file
@ -0,0 +1,26 @@
|
||||
.placeholders {
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.art {
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
width: 75%;
|
||||
height: auto;
|
||||
|
||||
/* doiuse-disable viewport-units */
|
||||
|
||||
max-width: 25vw;
|
||||
|
||||
/* doiuse-enable viewport-units */
|
||||
}
|
||||
|
||||
.art:hover {
|
||||
transform: scale(1.1);
|
||||
cursor: pointer;
|
||||
}
|
7
app/styles/elements/Pagination.scss
Normal file
7
app/styles/elements/Pagination.scss
Normal file
@ -0,0 +1,7 @@
|
||||
.nav {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
100
app/styles/layouts/Sidebar.scss
Normal file
100
app/styles/layouts/Sidebar.scss
Normal file
@ -0,0 +1,100 @@
|
||||
$background: #333;
|
||||
$hoverBackground: #222;
|
||||
$activeBackground: $hoverBackground;
|
||||
$foreground: white;
|
||||
$lightgrey: #eee;
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||
background-color: $background;
|
||||
color: white;
|
||||
border-right: 1px solid $lightgrey;
|
||||
}
|
||||
|
||||
/* Sidebar elements */
|
||||
.link {
|
||||
color: $foreground;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.link:hover,
|
||||
.link:focus {
|
||||
color: $foreground;
|
||||
background-color: $hoverBackground !important; /* TODO: important */
|
||||
}
|
||||
|
||||
.active {
|
||||
composes: link;
|
||||
background-color: $activeBackground;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.imgTitle {
|
||||
height: 46px;
|
||||
}
|
||||
|
||||
/* Sidebar navigation */
|
||||
.icon-navbar {
|
||||
background-color: #555;
|
||||
font-size: 1.25em;
|
||||
border: none;
|
||||
|
||||
.nav {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
vertical-align: top;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Main content
|
||||
*/
|
||||
.main-panel {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Media queries
|
||||
* TODO: Sidebar responsiveness
|
||||
*/
|
||||
/*@media (max-width: 991px) {
|
||||
.sidebar,
|
||||
.sidebar .navbar .container-fluid {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.sidebar .nav-list {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sidebar .nav-sidebar > li > a {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.sidebar .nav-list > li > a {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.main-panel {
|
||||
padding-right: 40px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
}*/
|
@ -4,7 +4,6 @@ then
|
||||
against=HEAD
|
||||
else
|
||||
# Initial commit
|
||||
# TODO
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@ -17,5 +16,7 @@ then
|
||||
fi
|
||||
|
||||
echo "Rebuilding dist JavaScript files…"
|
||||
NODE_ENV=production npm run build
|
||||
export NODE_ENV=production
|
||||
npm run clean
|
||||
npm run build
|
||||
git add app/dist
|
||||
|
@ -1,8 +1,3 @@
|
||||
// Export
|
||||
import "bootstrap";
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import "./app/styles/ampache.css";
|
||||
|
||||
// Handle app init
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
10
package.json
10
package.json
@ -11,12 +11,13 @@
|
||||
"watch": "./node_modules/.bin/webpack --progress --watch",
|
||||
"prod": "NODE_ENV=production ./node_modules/.bin/webpack --progress",
|
||||
"extractTranslations": "./node_modules/.bin/babel-node scripts/extractTranslations.js",
|
||||
"clean": "./node_modules/.bin/rimraf app/dist"
|
||||
"clean": "./node_modules/.bin/rimraf .cache && ./node_modules/.bin/rimraf app/dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.9.1",
|
||||
"babel-preset-es2015": "^6.9.0",
|
||||
"bootstrap": "^3.3.6",
|
||||
"bootstrap-loader": "^1.0.10",
|
||||
"bootstrap-sass": "^3.3.7",
|
||||
"fuse.js": "^2.3.0",
|
||||
"html5shiv": "^3.7.3",
|
||||
"humps": "^1.1.0",
|
||||
@ -29,6 +30,7 @@
|
||||
"jssha": "^2.1.0",
|
||||
"lodash": "^4.13.1",
|
||||
"react": "^15.2.0",
|
||||
"react-css-modules": "^3.7.9",
|
||||
"react-dom": "^15.2.0",
|
||||
"react-intl": "^2.1.3",
|
||||
"react-redux": "^4.4.5",
|
||||
@ -44,7 +46,6 @@
|
||||
"babel-core": "^6.10.4",
|
||||
"babel-loader": "^6.2.4",
|
||||
"babel-plugin-react-intl": "^2.1.3",
|
||||
"babel-plugin-transform-runtime": "^6.12.0",
|
||||
"babel-preset-react": "^6.11.1",
|
||||
"css-loader": "^0.23.1",
|
||||
"doiuse": "^2.4.1",
|
||||
@ -55,6 +56,7 @@
|
||||
"extract-text-webpack-plugin": "^1.0.1",
|
||||
"file-loader": "^0.9.0",
|
||||
"glob": "^7.0.5",
|
||||
"node-sass": "^3.8.0",
|
||||
"postcss": "^5.1.0",
|
||||
"postcss-loader": "^0.9.1",
|
||||
"postcss-reporter": "^1.4.1",
|
||||
@ -63,7 +65,9 @@
|
||||
"react-intl-webpack-plugin": "0.0.3",
|
||||
"redbox-react": "^1.2.10",
|
||||
"redux-logger": "^2.6.1",
|
||||
"resolve-url-loader": "^1.6.0",
|
||||
"rimraf": "^2.5.4",
|
||||
"sass-loader": "^4.0.0",
|
||||
"style-loader": "^0.13.1",
|
||||
"stylelint": "^7.0.3",
|
||||
"stylelint-config-standard": "^11.0.0",
|
||||
|
@ -11,7 +11,7 @@ var browsers = ["ie >= 9", "> 1%", "last 3 versions", "not op_mini all"];
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
"index": ["babel-polyfill", "./index.js"],
|
||||
"index": ["babel-polyfill", "bootstrap-loader", "./app/styles/common/index.js", "./index.js"],
|
||||
"fix.ie9": "./fix.ie9.js"
|
||||
},
|
||||
|
||||
@ -40,24 +40,22 @@ module.exports = {
|
||||
},
|
||||
include: __dirname
|
||||
},
|
||||
// Do not postcss vendor modules
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: /node_modules/,
|
||||
loader: ExtractTextPlugin.extract("style-loader", "css-loader!postcss-loader")
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: /app/,
|
||||
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader")
|
||||
loader: ExtractTextPlugin.extract(
|
||||
"style-loader",
|
||||
"css-loader?modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]" +
|
||||
"!postcss-loader"
|
||||
)
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
loader: ExtractTextPlugin.extract("style-loader", "css-loader!sass-loader")
|
||||
loader: ExtractTextPlugin.extract(
|
||||
"style-loader",
|
||||
"css-loader?modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]" +
|
||||
// TODO: "!postcss-loader" +
|
||||
"!sass-loader"
|
||||
)
|
||||
},
|
||||
{
|
||||
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
|
||||
@ -86,9 +84,7 @@ module.exports = {
|
||||
new ExtractTextPlugin("style.css", { allChunks: true })
|
||||
],
|
||||
|
||||
postcss: function () {
|
||||
return [doiuse({ browsers: browsers }), stylelint, precss, autoprefixer({ browsers: browsers }), postcssReporter({ throwError: true, clearMessages: true })];
|
||||
},
|
||||
postcss: [doiuse({ browsers: browsers }), stylelint, precss, autoprefixer({ browsers: browsers }), postcssReporter({ throwError: true, clearMessages: true })],
|
||||
|
||||
resolve: {
|
||||
// Include empty string "" to resolve files by their explicit extension
|
||||
|
@ -4,9 +4,9 @@ var config = require("./webpack.config.base.js");
|
||||
config.devtool = "cheap-module-eval-source-map";
|
||||
|
||||
// necessary for hot reloading with IE:
|
||||
config.entry.eventsourcePolyfill = 'eventsource-polyfill';
|
||||
config.entry.index.splice(1, 0, 'eventsource-polyfill');
|
||||
// listen to code updates emitted by hot middleware:
|
||||
config.entry.webpackHotMiddlewareClient = 'webpack-hot-middleware/client';
|
||||
config.entry.index.splice(2, 0, 'webpack-hot-middleware/client');
|
||||
|
||||
config.plugins = config.plugins.concat([
|
||||
new webpack.HotModuleReplacementPlugin()
|
||||
|
Loading…
Reference in New Issue
Block a user