Seed Support #56
23 changed files with 272 additions and 275 deletions
39
ui/js/app.js
39
ui/js/app.js
|
@ -244,51 +244,50 @@ var App = React.createClass({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getMainContent: function()
|
getContentAndAddress: function()
|
||||||
{
|
{
|
||||||
switch(this.state.viewingPage)
|
switch(this.state.viewingPage)
|
||||||
{
|
{
|
||||||
case 'settings':
|
case 'settings':
|
||||||
return <SettingsPage />;
|
return ["Settings", "icon-gear", <SettingsPage />];
|
||||||
case 'help':
|
case 'help':
|
||||||
return <HelpPage />;
|
return ["Help", "icon-question", <HelpPage />];
|
||||||
case 'report':
|
case 'report':
|
||||||
return <ReportPage />;
|
return ['Report', 'icon-file', <ReportPage />];
|
||||||
case 'downloaded':
|
case 'downloaded':
|
||||||
return <FileListDownloaded />;
|
return ["Downloads & Purchases", "icon-folder", <FileListDownloaded />];
|
||||||
case 'published':
|
case 'published':
|
||||||
return <FileListPublished />;
|
return ["Publishes", "icon-folder", <FileListPublished />];
|
||||||
case 'start':
|
case 'start':
|
||||||
return <StartPage />;
|
return ["Start", "icon-file", <StartPage />];
|
||||||
case 'rewards':
|
case 'rewards':
|
||||||
return <RewardsPage />;
|
return ["Rewards", "icon-bank", <RewardsPage />];
|
||||||
case 'wallet':
|
case 'wallet':
|
||||||
case 'send':
|
case 'send':
|
||||||
case 'receive':
|
case 'receive':
|
||||||
return <WalletPage viewingPage={this.state.viewingPage} />;
|
return [this.state.viewingPage.charAt(0).toUpperCase() + this.state.viewingPage.slice(1), "icon-bank", <WalletPage viewingPage={this.state.viewingPage} />]
|
||||||
case 'show':
|
case 'show':
|
||||||
return <ShowPage uri={this.state.pageArgs} />;
|
return [this.state.pageArgs, "icon-file", <ShowPage uri={this.state.pageArgs} />];
|
||||||
case 'publish':
|
case 'publish':
|
||||||
return <PublishPage />;
|
return ["Publish", "icon-upload", <PublishPage />];
|
||||||
case 'developer':
|
case 'developer':
|
||||||
return <DeveloperPage />;
|
return ["Developer", "icon-file", <DeveloperPage />];
|
||||||
case 'discover':
|
case 'discover':
|
||||||
default:
|
default:
|
||||||
return <DiscoverPage showWelcome={this.state.justRegistered} {... this.state.pageArgs !== null ? {query: this.state.pageArgs} : {} } />;
|
return ["Home", "icon-home", <DiscoverPage showWelcome={this.state.justRegistered} {... this.state.pageArgs !== null ? {query: this.state.pageArgs} : {} } />];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
var mainContent = this.getMainContent(),
|
let [address, wunderBarIcon, mainContent] = this.getContentAndAddress(),
|
||||||
headerLinks = this.getHeaderLinks(),
|
headerLinks = this.getHeaderLinks();
|
||||||
searchQuery = this.state.viewingPage == 'discover' && this.state.pageArgs ? this.state.pageArgs : '';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
this._fullScreenPages.includes(this.state.viewingPage) ?
|
this._fullScreenPages.includes(this.state.viewingPage) ?
|
||||||
mainContent :
|
mainContent :
|
||||||
<div id="window" className={ this.state.drawerOpen ? 'drawer-open' : 'drawer-closed' }>
|
<div id="window">
|
||||||
<Drawer onCloseDrawer={this.closeDrawer} viewingPage={this.state.viewingPage} />
|
<Header onOpenDrawer={this.openDrawer} address={address} wunderBarIcon={wunderBarIcon}
|
||||||
|
onSearch={this.onSearch} links={headerLinks} viewingPage={this.state.viewingPage} />
|
||||||
<div id="main-content" className={ headerLinks ? 'with-sub-nav' : 'no-sub-nav' }>
|
<div id="main-content" className={ headerLinks ? 'with-sub-nav' : 'no-sub-nav' }>
|
||||||
<Header onOpenDrawer={this.openDrawer} initialQuery={searchQuery} onSearch={this.onSearch} links={headerLinks} viewingPage={this.state.viewingPage} />
|
|
||||||
{mainContent}
|
{mainContent}
|
||||||
</div>
|
</div>
|
||||||
<Modal isOpen={this.state.modal == 'upgrade'} contentLabel="Update available"
|
<Modal isOpen={this.state.modal == 'upgrade'} contentLabel="Update available"
|
||||||
|
|
|
@ -1,72 +1,135 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Link} from './link.js';
|
import {Link} from './link.js';
|
||||||
import {Icon} from './common.js';
|
import {Icon, CreditAmount} from './common.js';
|
||||||
|
|
||||||
var Header = React.createClass({
|
var Header = React.createClass({
|
||||||
|
_balanceSubscribeId: null,
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
title: "LBRY",
|
balance: 0
|
||||||
isScrolled: false
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
componentWillMount: function() {
|
|
||||||
new MutationObserver((mutations) => {
|
|
||||||
this.setState({ title: mutations[0].target.textContent });
|
|
||||||
}).observe(
|
|
||||||
document.querySelector('title'),
|
|
||||||
{ subtree: true, characterData: true, childList: true }
|
|
||||||
);
|
|
||||||
},
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
document.addEventListener('scroll', this.handleScroll);
|
this._balanceSubscribeId = lbry.balanceSubscribe((balance) => {
|
||||||
},
|
this.setState({ balance: balance });
|
||||||
componentWillUnmount: function() {
|
|
||||||
document.removeEventListener('scroll', this.handleScroll);
|
|
||||||
if (this.userTypingTimer)
|
|
||||||
{
|
|
||||||
clearTimeout(this.userTypingTimer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleScroll: function() {
|
|
||||||
this.setState({
|
|
||||||
isScrolled: document.body.scrollTop > 0
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onQueryChange: function(event) {
|
componentWillUnmount: function() {
|
||||||
|
if (this._balanceSubscribeId) {
|
||||||
if (this.userTypingTimer)
|
lbry.balanceUnsubscribe(this._balanceSubscribeId)
|
||||||
{
|
|
||||||
clearTimeout(this.userTypingTimer);
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
return <div>
|
||||||
|
<header id="header">
|
||||||
|
<div className="header__item">
|
||||||
|
<Link onClick={() => { history.back() }} button="alt button--flat" icon="icon-arrow-left" />
|
||||||
|
</div>
|
||||||
|
<div className="header__item">
|
||||||
|
<Link href="?discover" button="alt button--flat" icon="icon-home" />
|
||||||
|
</div>
|
||||||
|
<div className="header__item header__item--wunderbar">
|
||||||
|
<WunderBar address={this.props.address} icon={this.props.wunderBarIcon} onSearch={this.props.onSearch} />
|
||||||
|
</div>
|
||||||
|
<div className="header__item">
|
||||||
|
<Link href="?wallet" button="text" icon="icon-bank" label={lbry.formatCredits(this.state.balance, 1)} ></Link>
|
||||||
|
</div>
|
||||||
|
<div className="header__item">
|
||||||
|
<Link button="primary button--flat" href="?publish" icon="icon-upload" label="Publish" />
|
||||||
|
</div>
|
||||||
|
<div className="header__item">
|
||||||
|
<Link button="alt button--flat" href="?downloaded" icon="icon-folder" />
|
||||||
|
</div>
|
||||||
|
<div className="header__item">
|
||||||
|
<Link button="alt button--flat" href="?settings" icon="icon-gear" />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
{this.props.links ?
|
||||||
|
<SubHeader links={this.props.links} viewingPage={this.props.viewingPage} /> :
|
||||||
|
''}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let WunderBar = React.createClass({
|
||||||
|
_userTypingTimer: null,
|
||||||
|
_input: null,
|
||||||
|
_stateBeforeSearch: null,
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
address: this.props.address,
|
||||||
|
icon: this.props.icon
|
||||||
|
};
|
||||||
|
},
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
if (this.userTypingTimer) {
|
||||||
|
clearTimeout(this._userTypingTimer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onChange: function(event) {
|
||||||
|
|
||||||
|
if (this._userTypingTimer)
|
||||||
|
{
|
||||||
|
clearTimeout(this._userTypingTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ address: event.target.value })
|
||||||
|
|
||||||
//@TODO: Switch to React.js timing
|
//@TODO: Switch to React.js timing
|
||||||
var searchTerm = event.target.value;
|
var searchTerm = event.target.value;
|
||||||
this.userTypingTimer = setTimeout(() => {
|
|
||||||
|
this._userTypingTimer = setTimeout(() => {
|
||||||
this.props.onSearch(searchTerm);
|
this.props.onSearch(searchTerm);
|
||||||
}, 800); // 800ms delay, tweak for faster/slower
|
}, 800); // 800ms delay, tweak for faster/slower
|
||||||
|
|
||||||
},
|
},
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (nextProps.address !== this.state.address || nextProps.icon !== this.state.icon) {
|
||||||
|
this.setState({ address: nextProps.address, icon: nextProps.icon });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFocus: function() {
|
||||||
|
this._stateBeforeSearch = this.state;
|
||||||
|
let newState = {
|
||||||
|
icon: "icon-search"
|
||||||
|
}
|
||||||
|
// this._input.value = ""; //trigger placeholder
|
||||||
|
this._focusPending = true;
|
||||||
|
if (!this.state.address.match(/^lbry:\/\//)) //onFocus, if they are not on an exact URL, clear the bar
|
||||||
|
{
|
||||||
|
newState.address = "";
|
||||||
|
}
|
||||||
|
this.setState(newState);
|
||||||
|
},
|
||||||
|
onBlur: function() {
|
||||||
|
this.setState(this._stateBeforeSearch);
|
||||||
|
this._input.value = this.state.address;
|
||||||
|
},
|
||||||
|
componentDidUpdate: function() {
|
||||||
|
this._input.value = this.state.address;
|
||||||
|
if (this._input && this._focusPending) {
|
||||||
|
this._input.select();
|
||||||
|
this._focusPending = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onReceiveRef: function(ref) {
|
||||||
|
this._input = ref;
|
||||||
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return <div className="wunderbar">
|
||||||
<header id="header" className={ (this.state.isScrolled ? 'header-scrolled' : 'header-unscrolled') + ' ' + (this.props.links ? 'header-with-subnav' : 'header-no-subnav') }>
|
{this.state.icon ? <Icon fixed icon={this.state.icon} /> : '' }
|
||||||
<div className="header-top-bar">
|
<input className="wunderbar__input" type="search" placeholder="Type a LBRY address or search term"
|
||||||
<Link onClick={this.props.onOpenDrawer} icon="icon-bars" className="open-drawer-link" />
|
ref={this.onReceiveRef}
|
||||||
<h1>{ this.state.title }</h1>
|
onFocus={this.onFocus}
|
||||||
<div className="header-search">
|
onBlur={this.onBlur}
|
||||||
<Icon icon="icon-search" />
|
onChange={this.onChange}
|
||||||
<input type="search" onChange={this.onQueryChange} defaultValue={this.props.initialQuery}
|
value={ this.state.address }
|
||||||
placeholder="Find movies, music, games, and more"/>
|
placeholder="Find movies, music, games, and more" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{
|
|
||||||
this.props.links ?
|
|
||||||
<SubHeader links={this.props.links} viewingPage={this.props.viewingPage} /> :
|
|
||||||
''
|
|
||||||
}
|
|
||||||
</header>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
var SubHeader = React.createClass({
|
var SubHeader = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
|
|
|
@ -41,7 +41,7 @@ export let Link = React.createClass({
|
||||||
content = (
|
content = (
|
||||||
<span {... 'button' in this.props ? {className: 'button__content'} : {}}>
|
<span {... 'button' in this.props ? {className: 'button__content'} : {}}>
|
||||||
{'icon' in this.props ? <Icon icon={this.props.icon} fixed={true} /> : null}
|
{'icon' in this.props ? <Icon icon={this.props.icon} fixed={true} /> : null}
|
||||||
{<span className="link-label">{this.props.label}</span>}
|
{this.props.label ? <span className="link-label">{this.props.label}</span> : null}
|
||||||
{'badge' in this.props ? <span className="badge">{this.props.badge}</span> : null}
|
{'badge' in this.props ? <span className="badge">{this.props.badge}</span> : null}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
|
@ -627,18 +627,18 @@ lbry.claim_list_mine = function(params={}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.resolve = function(params={}) {
|
lbry.resolve = function(params={}) {
|
||||||
const claimCacheKey = 'resolve_claim_cache',
|
const claimCacheKey = 'resolve_claim_cache3',
|
||||||
claimCache = getSession(claimCacheKey, {})
|
claimCache = getLocal(claimCacheKey, {})
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!params.uri) {
|
if (!params.uri) {
|
||||||
throw "Resolve has hacked cache on top of it that requires a URI"
|
throw "Resolve has hacked cache on top of it that requires a URI"
|
||||||
}
|
}
|
||||||
if (params.uri && claimCache[params.uri]) {
|
if (params.uri && claimCache[params.uri] !== undefined) {
|
||||||
resolve(claimCache[params.uri]);
|
resolve(claimCache[params.uri]);
|
||||||
} else {
|
} else {
|
||||||
lbry.call('resolve', params, function(data) {
|
lbry.call('resolve', params, function(data) {
|
||||||
claimCache[params.uri] = data;
|
claimCache[params.uri] = data;
|
||||||
setSession(claimCacheKey, claimCache)
|
setLocal(claimCacheKey, claimCache)
|
||||||
resolve(data)
|
resolve(data)
|
||||||
}, reject)
|
}, reject)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,11 @@ const lbryio = {
|
||||||
_accessToken: getLocal('accessToken'),
|
_accessToken: getLocal('accessToken'),
|
||||||
_authenticationPromise: null,
|
_authenticationPromise: null,
|
||||||
_user : null,
|
_user : null,
|
||||||
enabled: true
|
enabled: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const CONNECTION_STRING = process.env.LBRY_APP_API_URL ? process.env.LBRY_APP_API_URL : 'https://api.lbry.io/';
|
// const CONNECTION_STRING = process.env.LBRY_APP_API_URL ? process.env.LBRY_APP_API_URL : 'https://api.lbry.io/';
|
||||||
|
const CONNECTION_STRING = 'https://api.lbry.io/';
|
||||||
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
|
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
|
||||||
|
|
||||||
lbryio.getExchangeRates = function() {
|
lbryio.getExchangeRates = function() {
|
||||||
|
|
|
@ -164,7 +164,7 @@ var DiscoverPage = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
document.title = "Discover";
|
document.title = "Home";
|
||||||
|
|
||||||
if (this.props.query) {
|
if (this.props.query) {
|
||||||
// Rendering with a query already typed
|
// Rendering with a query already typed
|
||||||
|
|
|
@ -19,7 +19,6 @@ export let FileListDownloaded = React.createClass({
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this._isMounted = true;
|
this._isMounted = true;
|
||||||
document.title = "Downloaded Files";
|
|
||||||
|
|
||||||
lbry.claim_list_mine().then((myClaimInfos) => {
|
lbry.claim_list_mine().then((myClaimInfos) => {
|
||||||
if (!this._isMounted) { return; }
|
if (!this._isMounted) { return; }
|
||||||
|
|
|
@ -348,9 +348,6 @@ var PublishPage = React.createClass({
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
this._updateChannelList();
|
this._updateChannelList();
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
|
||||||
document.title = "Publish";
|
|
||||||
},
|
|
||||||
componentDidUpdate: function() {
|
componentDidUpdate: function() {
|
||||||
},
|
},
|
||||||
onFileChange: function() {
|
onFileChange: function() {
|
||||||
|
|
|
@ -17,7 +17,7 @@ var SettingsPage = React.createClass({
|
||||||
setClientSetting: function(name, value) {
|
setClientSetting: function(name, value) {
|
||||||
lbry.setClientSetting(name, value)
|
lbry.setClientSetting(name, value)
|
||||||
this._onSettingSaveSuccess()
|
this._onSettingSaveSuccess()
|
||||||
},
|
},
|
||||||
onRunOnStartChange: function (event) {
|
onRunOnStartChange: function (event) {
|
||||||
this.setDaemonSetting('run_on_startup', event.target.checked);
|
this.setDaemonSetting('run_on_startup', event.target.checked);
|
||||||
},
|
},
|
||||||
|
|
|
@ -60,7 +60,6 @@ let ShowPage = React.createClass({
|
||||||
},
|
},
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
this._uri = lbryuri.normalize(this.props.uri);
|
this._uri = lbryuri.normalize(this.props.uri);
|
||||||
document.title = this._uri;
|
|
||||||
|
|
||||||
lbry.resolve({uri: this._uri}).then(({ claim: {txid, nout, has_signature, signature_is_valid, value: {stream: {metadata, source: {contentType}}}}}) => {
|
lbry.resolve({uri: this._uri}).then(({ claim: {txid, nout, has_signature, signature_is_valid, value: {stream: {metadata, source: {contentType}}}}}) => {
|
||||||
const outpoint = txid + ':' + nout;
|
const outpoint = txid + ':' + nout;
|
||||||
|
@ -71,6 +70,8 @@ let ShowPage = React.createClass({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.title = metadata.title ? metadata.title : this._uri;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
outpoint: outpoint,
|
outpoint: outpoint,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
|
@ -91,6 +92,7 @@ let ShowPage = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
const metadata = this.state.metadata;
|
const metadata = this.state.metadata;
|
||||||
const title = metadata ? this.state.metadata.title : this._uri;
|
const title = metadata ? this.state.metadata.title : this._uri;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="constrained-page">
|
<main className="constrained-page">
|
||||||
<section className="show-page-media">
|
<section className="show-page-media">
|
||||||
|
|
|
@ -5,9 +5,6 @@ var StartPage = React.createClass({
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
lbry.stop();
|
lbry.stop();
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
|
||||||
document.title = "LBRY is Closed";
|
|
||||||
},
|
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<main className="page">
|
<main className="page">
|
||||||
|
|
|
@ -270,9 +270,6 @@ var WalletPage = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
viewingPage: React.PropTypes.string,
|
viewingPage: React.PropTypes.string,
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
|
||||||
document.title = "My Wallet";
|
|
||||||
},
|
|
||||||
/*
|
/*
|
||||||
Below should be refactored so that balance is shared all of wallet page. Or even broader?
|
Below should be refactored so that balance is shared all of wallet page. Or even broader?
|
||||||
What is the proper React pattern for sharing a global state like balance?
|
What is the proper React pattern for sharing a global state like balance?
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
* Thin wrapper around localStorage.getItem(). Parses JSON and returns undefined if the value
|
* Thin wrapper around localStorage.getItem(). Parses JSON and returns undefined if the value
|
||||||
* is not set yet.
|
* is not set yet.
|
||||||
*/
|
*/
|
||||||
export function getLocal(key) {
|
export function getLocal(key, fallback=undefined) {
|
||||||
const itemRaw = localStorage.getItem(key);
|
const itemRaw = localStorage.getItem(key);
|
||||||
return itemRaw === null ? undefined : JSON.parse(itemRaw);
|
return itemRaw === null ? fallback : JSON.parse(itemRaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,44 +11,12 @@ body
|
||||||
line-height: $font-line-height;
|
line-height: $font-line-height;
|
||||||
}
|
}
|
||||||
|
|
||||||
$drawer-width: 220px;
|
#window
|
||||||
|
|
||||||
#drawer
|
|
||||||
{
|
{
|
||||||
width: $drawer-width;
|
|
||||||
position: fixed;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
left: 0;
|
background: $color-canvas;
|
||||||
top: 0;
|
|
||||||
background: $color-bg;
|
|
||||||
z-index: 3;
|
|
||||||
.drawer-item
|
|
||||||
{
|
|
||||||
display: block;
|
|
||||||
padding: $spacing-vertical / 2;
|
|
||||||
font-size: 1.2em;
|
|
||||||
height: $spacing-vertical * 1.5;
|
|
||||||
.icon
|
|
||||||
{
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
.link-label
|
|
||||||
{
|
|
||||||
line-height: $spacing-vertical * 1.5;
|
|
||||||
}
|
|
||||||
.badge
|
|
||||||
{
|
|
||||||
float: right;
|
|
||||||
margin-top: $spacing-vertical * 0.25 - 2;
|
|
||||||
background: $color-money;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.drawer-item-selected
|
|
||||||
{
|
|
||||||
background: $color-canvas;
|
|
||||||
color: $color-primary;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge
|
.badge
|
||||||
{
|
{
|
||||||
background: $color-money;
|
background: $color-money;
|
||||||
|
@ -62,119 +30,9 @@ $drawer-width: 220px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: $color-money;
|
color: $color-money;
|
||||||
}
|
}
|
||||||
#drawer-handle
|
|
||||||
{
|
|
||||||
padding: $spacing-vertical / 2;
|
|
||||||
max-height: $height-header - $spacing-vertical;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#window
|
|
||||||
{
|
|
||||||
position: relative; /*window has it's own z-index inside of it*/
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
#window.drawer-closed
|
|
||||||
{
|
|
||||||
#drawer { display: none }
|
|
||||||
}
|
|
||||||
#window.drawer-open
|
|
||||||
{
|
|
||||||
#main-content { margin-left: $drawer-width; }
|
|
||||||
.open-drawer-link { display: none }
|
|
||||||
#header { padding-left: $drawer-width + $spacing-vertical / 2; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#header
|
|
||||||
{
|
|
||||||
background: $color-primary;
|
|
||||||
color: white;
|
|
||||||
&.header-no-subnav {
|
|
||||||
height: $height-header;
|
|
||||||
}
|
|
||||||
&.header-with-subnav {
|
|
||||||
height: $height-header * 2;
|
|
||||||
}
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 2;
|
|
||||||
box-sizing: border-box;
|
|
||||||
h1 { font-size: 1.8em; line-height: $height-header - $spacing-vertical; display: inline-block; float: left; }
|
|
||||||
&.header-scrolled
|
|
||||||
{
|
|
||||||
box-shadow: $default-box-shadow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.header-top-bar
|
|
||||||
{
|
|
||||||
padding: $spacing-vertical / 2;
|
|
||||||
}
|
|
||||||
.header-search
|
|
||||||
{
|
|
||||||
margin-left: 60px;
|
|
||||||
$padding-adjust: 36px;
|
|
||||||
text-align: center;
|
|
||||||
.icon {
|
|
||||||
position: absolute;
|
|
||||||
top: $spacing-vertical * 1.5 / 2 + 2px; //hacked
|
|
||||||
margin-left: -$padding-adjust + 14px; //hacked
|
|
||||||
}
|
|
||||||
input[type="search"] {
|
|
||||||
position: relative;
|
|
||||||
left: -$padding-adjust;
|
|
||||||
background: rgba(255, 255, 255, 0.3);
|
|
||||||
color: white;
|
|
||||||
width: 400px;
|
|
||||||
height: $spacing-vertical * 1.5;
|
|
||||||
line-height: $spacing-vertical * 1.5;
|
|
||||||
padding-left: $padding-adjust + 3;
|
|
||||||
padding-right: 3px;
|
|
||||||
@include border-radius(2px);
|
|
||||||
@include placeholder-color(#e8e8e8);
|
|
||||||
&:focus {
|
|
||||||
box-shadow: $focus-box-shadow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.sub-header
|
|
||||||
{
|
|
||||||
background: $color-primary;
|
|
||||||
text-transform: uppercase;
|
|
||||||
padding: $spacing-vertical / 2;
|
|
||||||
> a
|
|
||||||
{
|
|
||||||
$sub-header-selected-underline-height: 2px;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 15px;
|
|
||||||
padding: 0 5px;
|
|
||||||
line-height: $height-header - $spacing-vertical - $sub-header-selected-underline-height;
|
|
||||||
color: #e8e8e8;
|
|
||||||
&:first-child
|
|
||||||
{
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
&:last-child
|
|
||||||
{
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
&.sub-header-selected
|
|
||||||
{
|
|
||||||
border-bottom: $sub-header-selected-underline-height solid #fff;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
&:hover
|
|
||||||
{
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#main-content
|
#main-content
|
||||||
{
|
{
|
||||||
background: $color-canvas;
|
|
||||||
&.no-sub-nav
|
&.no-sub-nav
|
||||||
{
|
{
|
||||||
min-height: calc(100vh - 60px); //should be -$height-header, but I'm dumb I guess? It wouldn't work
|
min-height: calc(100vh - 60px); //should be -$height-header, but I'm dumb I guess? It wouldn't work
|
||||||
|
@ -182,8 +40,7 @@ nav.sub-header
|
||||||
}
|
}
|
||||||
&.with-sub-nav
|
&.with-sub-nav
|
||||||
{
|
{
|
||||||
min-height: calc(100vh - 120px); //should be -$height-header, but I'm dumb I guess? It wouldn't work
|
min-height: calc(100vh - 60px); //should be -$height-header, but I'm dumb I guess? It wouldn't work
|
||||||
main { margin-top: $height-header * 2; }
|
|
||||||
}
|
}
|
||||||
main
|
main
|
||||||
{
|
{
|
||||||
|
@ -195,26 +52,4 @@ nav.sub-header
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$header-icon-size: 1.5em;
|
|
||||||
|
|
||||||
.open-drawer-link, .close-drawer-link
|
|
||||||
{
|
|
||||||
display: inline-block;
|
|
||||||
font-size: $header-icon-size;
|
|
||||||
padding: 2px 6px 0 6px;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
.close-lbry-link
|
|
||||||
{
|
|
||||||
font-size: $header-icon-size;
|
|
||||||
float: right;
|
|
||||||
padding: 0 6px 0 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-screen
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
|
@ -34,8 +34,8 @@ $height-header: $spacing-vertical * 2.5;
|
||||||
$height-button: $spacing-vertical * 1.5;
|
$height-button: $spacing-vertical * 1.5;
|
||||||
$height-video-embedded: $width-page-constrained * 9 / 16;
|
$height-video-embedded: $width-page-constrained * 9 / 16;
|
||||||
|
|
||||||
$default-box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
|
$box-shadow-layer: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
|
||||||
$focus-box-shadow: 2px 4px 4px 0 rgba(0,0,0,.14),2px 5px 3px -2px rgba(0,0,0,.2),2px 3px 7px 0 rgba(0,0,0,.12);
|
$box-shadow-focus: 2px 4px 4px 0 rgba(0,0,0,.14),2px 5px 3px -2px rgba(0,0,0,.2),2px 3px 7px 0 rgba(0,0,0,.12);
|
||||||
|
|
||||||
$transition-standard: .225s ease;
|
$transition-standard: .225s ease;
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,6 @@
|
||||||
transform: translate(0, 0);
|
transform: translate(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-mega
|
|
||||||
{
|
|
||||||
font-size: 200px;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
|
/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
|
||||||
readers do not read off random characters that represent icons */
|
readers do not read off random characters that represent icons */
|
||||||
.icon-glass:before {
|
.icon-glass:before {
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
@import "component/_file-actions.scss";
|
@import "component/_file-actions.scss";
|
||||||
@import "component/_file-tile.scss";
|
@import "component/_file-tile.scss";
|
||||||
@import "component/_form-field.scss";
|
@import "component/_form-field.scss";
|
||||||
|
@import "component/_header.scss";
|
||||||
@import "component/_menu.scss";
|
@import "component/_menu.scss";
|
||||||
@import "component/_tooltip.scss";
|
@import "component/_tooltip.scss";
|
||||||
@import "component/_load-screen.scss";
|
@import "component/_load-screen.scss";
|
||||||
|
|
|
@ -34,6 +34,11 @@ $button-focus-shift: 12%;
|
||||||
{
|
{
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
|
.icon:only-child
|
||||||
|
{
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.button-block
|
.button-block
|
||||||
{
|
{
|
||||||
|
@ -49,17 +54,17 @@ $button-focus-shift: 12%;
|
||||||
$color-button-text: white;
|
$color-button-text: white;
|
||||||
color: darken($color-button-text, $button-focus-shift * 0.5);
|
color: darken($color-button-text, $button-focus-shift * 0.5);
|
||||||
background-color: $color-primary;
|
background-color: $color-primary;
|
||||||
box-shadow: $default-box-shadow;
|
box-shadow: $box-shadow-layer;
|
||||||
&:focus {
|
&:focus {
|
||||||
color: $color-button-text;
|
color: $color-button-text;
|
||||||
//box-shadow: $focus-box-shadow;
|
//box-shadow: $box-shadow-focus;
|
||||||
background-color: mix(black, $color-primary, $button-focus-shift)
|
background-color: mix(black, $color-primary, $button-focus-shift)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.button-alt
|
.button-alt
|
||||||
{
|
{
|
||||||
background-color: $color-bg-alt;
|
background-color: $color-bg-alt;
|
||||||
box-shadow: $default-box-shadow;
|
box-shadow: $box-shadow-layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-text
|
.button-text
|
||||||
|
@ -76,3 +81,7 @@ $button-focus-shift: 12%;
|
||||||
@include text-link(#aaa);
|
@include text-link(#aaa);
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
.button--flat
|
||||||
|
{
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ $padding-card-horizontal: $spacing-vertical * 2/3;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
max-width: $width-page-constrained;
|
max-width: $width-page-constrained;
|
||||||
background: $color-bg;
|
background: $color-bg;
|
||||||
box-shadow: $default-box-shadow;
|
box-shadow: $box-shadow-layer;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
margin-bottom: $spacing-vertical * 2/3;
|
margin-bottom: $spacing-vertical * 2/3;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
@ -86,7 +86,7 @@ $card-link-scaling: 1.1;
|
||||||
.card--link:hover {
|
.card--link:hover {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
box-shadow: $focus-box-shadow;
|
box-shadow: $box-shadow-focus;
|
||||||
transform: scale($card-link-scaling);
|
transform: scale($card-link-scaling);
|
||||||
transform-origin: 50% 50%;
|
transform-origin: 50% 50%;
|
||||||
overflow-x: visible;
|
overflow-x: visible;
|
||||||
|
|
103
ui/scss/component/_header.scss
Normal file
103
ui/scss/component/_header.scss
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
@import "../global";
|
||||||
|
|
||||||
|
$color-header: #666;
|
||||||
|
|
||||||
|
|
||||||
|
$header-icon-size: 1.5em;
|
||||||
|
|
||||||
|
.open-drawer-link, .close-drawer-link
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
font-size: $header-icon-size;
|
||||||
|
padding: 2px 6px 0 6px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header
|
||||||
|
{
|
||||||
|
color: $color-header;
|
||||||
|
background: #fff;
|
||||||
|
display: flex;
|
||||||
|
/*
|
||||||
|
&.header-no-subnav {
|
||||||
|
height: $height-header;
|
||||||
|
}
|
||||||
|
&.header-with-subnav {
|
||||||
|
height: $height-header * 2;
|
||||||
|
}*/
|
||||||
|
position: fixed;
|
||||||
|
box-shadow: $box-shadow-layer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 2;
|
||||||
|
padding: $spacing-vertical / 2;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.header__item {
|
||||||
|
flex: 0 0 content;
|
||||||
|
padding-left: $spacing-vertical / 4;
|
||||||
|
padding-right: $spacing-vertical / 4;
|
||||||
|
}
|
||||||
|
.header__item--wunderbar {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wunderbar
|
||||||
|
{
|
||||||
|
position: relative;
|
||||||
|
.icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
top: $spacing-vertical / 2 - 4px; //hacked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.wunderbar__input {
|
||||||
|
background: rgba(255, 255, 255, 0.7);
|
||||||
|
width: 100%;
|
||||||
|
color: $color-header;
|
||||||
|
height: $spacing-vertical * 1.5;
|
||||||
|
line-height: $spacing-vertical * 1.5;
|
||||||
|
padding-left: 38px;
|
||||||
|
padding-right: 5px;
|
||||||
|
border: 1px solid $color-text-dark;
|
||||||
|
@include border-radius(2px);
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
&:focus {
|
||||||
|
box-shadow: $box-shadow-focus;
|
||||||
|
border-color: $color-header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.sub-header
|
||||||
|
{
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: $spacing-vertical / 2;
|
||||||
|
> a
|
||||||
|
{
|
||||||
|
$sub-header-selected-underline-height: 2px;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 15px;
|
||||||
|
padding: 0 5px;
|
||||||
|
line-height: $height-header - $spacing-vertical - $sub-header-selected-underline-height;
|
||||||
|
color: $color-header;
|
||||||
|
&:first-child
|
||||||
|
{
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
&:last-child
|
||||||
|
{
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
$color-selected-subheader: darken($color-header, 20%);
|
||||||
|
&.sub-header-selected
|
||||||
|
{
|
||||||
|
border-bottom: $sub-header-selected-underline-height solid $color-selected-subheader;
|
||||||
|
color: $color-selected-subheader;
|
||||||
|
}
|
||||||
|
&:hover
|
||||||
|
{
|
||||||
|
color: $color-selected-subheader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ $border-radius-menu: 2px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
box-shadow: $default-box-shadow;
|
box-shadow: $box-shadow-layer;
|
||||||
border-radius: $border-radius-menu;
|
border-radius: $border-radius-menu;
|
||||||
padding-top: ($spacing-vertical / 5) 0px;
|
padding-top: ($spacing-vertical / 5) 0px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: $spacing-vertical;
|
padding: $spacing-vertical;
|
||||||
box-shadow: $default-box-shadow;
|
box-shadow: $box-shadow-layer;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
background-color: $color-bg;
|
background-color: $color-bg;
|
||||||
font-size: $font-size * 7/8;
|
font-size: $font-size * 7/8;
|
||||||
line-height: $font-line-height;
|
line-height: $font-line-height;
|
||||||
box-shadow: $default-box-shadow;
|
box-shadow: $box-shadow-layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip--header .tooltip__link {
|
.tooltip--header .tooltip__link {
|
||||||
|
|
Loading…
Reference in a new issue