UI: Menu and routing works with style reminiscent of aitestory
the build was successful

This commit is contained in:
2018-07-12 22:12:01 +02:00
parent 7adcdfe1e0
commit 576e1df001
19 changed files with 863 additions and 2481 deletions
-3
View File
@@ -1,3 +0,0 @@
.App {
}
+12 -3
View File
@@ -1,14 +1,23 @@
import React, { Component } from 'react';
import './App.css';
import React, { Component } from 'react'
import { Route, Router, Redirect, Switch } from "react-router-dom";
import StoryRoot from './routes/story/StoryRoot';
import LogsIndex from './routes/logs/LogsIndex';
class App extends Component {
render() {
return (
<div className="App">
<p>Hi</p>
<Switch>
<Redirect exact path="/" to="/story/" />
<Route path="/story/" component={StoryRoot}/>
<Route path="/logs/" component={LogsIndex}/>
</Switch>
</div>
);
}
}
export default App;
/* See ./common/Menu.js to change the top navbar */
+13
View File
@@ -0,0 +1,13 @@
import { ApolloClient } from "apollo-client"
import { HttpLink } from "apollo-link-http"
import { InMemoryCache } from "apollo-cache-inmemory"
const client = new ApolloClient({
link: new HttpLink({
uri: "/graphql"
}),
cache: new InMemoryCache(),
})
export default client
+76
View File
@@ -0,0 +1,76 @@
/* /story/ */
.theme-story .color-menu {
color: #789;
}
.theme-story .MenuLink.selected {
background-color: rgba(34, 187, 255, 0.125);
}
.theme-story .MenuLink:hover, .theme-story .Menu > .head-menu > a:hover {
background-color: rgba(34, 187, 255, 0.25);
}
.theme-story .color-primary {
color: #1CF;
}
.theme-story .color-text {
color: #ABC;
}
/* /logs/ */
.theme-logs .color-menu {
color: #998;
}
.theme-logs .MenuLink.selected {
background-color: rgba(255, 187, 34, 0.125);
}
.theme-logs .MenuLink:hover, .theme-logs .Menu > .head-menu > a:hover {
background-color: rgba(255, 187, 34, 0.25);
}
.theme-logs .color-primary {
color: #FC1;
}
.theme-logs .color-text {
color: #CCB;
}
/* /data/ */
.theme-data .color-menu {
color: #898;
}
.theme-data .MenuLink.selected {
background-color: rgba(35, 255, 128, 0.125);
}
.theme-data .MenuLink:hover, .theme-data .Menu > .head-menu > a:hover {
background-color: rgba(35, 255, 128, 0.25);
}
.theme-data .color-primary {
color: #1F8;
}
.theme-data .color-text {
color: #ACB;
}
/* Tag colors */
.color-tag-event {
color: #B77;
}
.color-tag-location {
color: #7B7;
}
.color-tag-organization {
color: #B7B;
}
.color-tag-series {
color: #BB7;
}
.color-tag-character {
color: #37B;
}
/* Danger */
.color-danger {
color: #FA3;
}
.MenuLink:hover, .MenuLink:hover .text, .Menu > .head-menu > a:hover {
color: #EEE !important;
}
+42
View File
@@ -0,0 +1,42 @@
import React, { Component } from 'react';
import './css/Background.css';
/**
* Background renders a full-size image that detects whether the source is
* landscape or portrait. It's a react-ification of AiteStory's background.
*/
export default class Background extends Component {
/**
* @param {HTMLImageElement} img
*/
onRef(img) {
if (img == null) {
return
}
if (img.complete && typeof(img.naturalHeight) !== 'undefined' && img.naturalHeight !== 0) {
const width = img.clientWidth
const height = img.clientHeight
if(width > (height * 1.50)) {
img.className += ' landscape'
}
} else {
img.onload = function () {
const width = img.clientWidth
const height = img.clientHeight
if(width > (height * 1.50)) {
img.className += ' landscape'
}
}
}
}
render() {
const src = this.props.src || "/assets/images/bg.png"
const opacity = this.props.opacity || 0.25
return <img style={{filter: `brightness(${(Number(opacity)) * 100}%)`}} ref={this.onRef.bind(this)} className="Background" src={src} />
}
}
+60
View File
@@ -0,0 +1,60 @@
import React, { Component } from 'react';
import { Link } from "react-router-dom";
import './css/Menu.css';
export default class Menu extends Component {
render() {
const { children } = this.props
return (
<nav className="Menu">
<h1 className="color-primary">Aite RP</h1>
<div className="head-menu">
<HeadMenuLink to="/story/">Story</HeadMenuLink>
<HeadMenuLink to="/logs/">Logs</HeadMenuLink>
</div>
<div className="content">
{ children }
</div>
</nav>
)
}
}
export function MenuLink({ icon, children, tagType, to, onClick }) {
const isSelected = window.location.pathname === to
const className = isSelected ? "selected" : "not-selected"
const textClass = isSelected ? "color-primary" : `color-tag-${tagType||"none"}`
const body = (
<div className={`MenuLink color-menu ${className}`} onClick={onClick}>
<div className="icon color-primary">{icon}</div>
<div className={`text ${textClass}`}>{children}</div>
</div>
)
if (to == null) {
return body
} else {
return <Link to={to}>{body}</Link>
}
}
export function MenuGap() {
return <div className="MenuGap"></div>
}
export function MenuHeader({children}) {
return <div className="MenuHeader color-menu">{children}</div>
}
export function HeadMenuLink({to, children}) {
const className = (window.location.pathname.startsWith(to) ? "color-primary" : "color-menu")
return <Link className={className} to={to}>{children}</Link>
}
export function MenuBlurb({children}) {
return <div className="MenuBlurb">{children}</div>
}
+22
View File
@@ -0,0 +1,22 @@
img.Background {
/* Set rules to fill background */
width: 100%;
/* Set up proportionate scaling */
min-height: 100%;
height: auto;
/* Set up positioning */
position: fixed;
top: 0;
left: 0;
z-index: -1;
user-select: none;
-webkit-user-drag: none;
}
img.Background.landscape {
/* Set rules to fill background */
height: 100%;
/* Set up proportionate scaling */
min-width: 100%;
width: auto;
}
+64
View File
@@ -0,0 +1,64 @@
.Menu {
position: sticky;
width: 28ch;
text-align: center;
}
.Menu > .head-menu {
padding: 0 2ch;
padding-bottom: 0.5em;
}
.Menu > .head-menu > a {
display: inline-block;
padding: 0.25em 1ch;
font-size: 0.75em;
}
.Menu > h1 {
padding: 1em 0 0em 0;
margin: 0;
font-size: 2.5em;
user-select: none;
}
.Menu > .content {
margin-top: 1em;
text-align: left;
}
.MenuLink {
width: 100%;
padding-left: 0.5ch;
font-size: 1.1em;
cursor: pointer;
}
.MenuLink .icon {
display: inline-block;
width: 3ch;
padding: 0.15em 0.125em;
text-align: center;
}
.MenuLink .text {
display: inline-block;
}
.MenuGap {
padding: 0.25em;
}
.MenuHeader {
opacity: 0.5;
user-select: none;
padding: 0.125em 1.5ch;
}
.MenuBlurb {
opacity: 0.5;
padding: 0.25em 1.5ch;
}
+6
View File
@@ -2,4 +2,10 @@ body {
margin: 0;
padding: 0;
font-family: sans-serif;
background-color: black;
color: white;
}
a {
text-decoration: none;
}
+19 -6
View File
@@ -1,8 +1,21 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom"
import { ApolloProvider } from "react-apollo";
import client from "./client";
import App from "./App"
import registerServiceWorker from "./registerServiceWorker"
import "./index.css"
import "./colors.css";
ReactDOM.render((
<ApolloProvider client={client}>
<BrowserRouter>
<App />
</BrowserRouter>
</ApolloProvider>
), document.getElementById('root'))
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
+14
View File
@@ -0,0 +1,14 @@
import React, { Component } from 'react';
import Background from '../../common/Background';
import LogsMenu from './LogsMenu';
export default class LogsIndex extends Component {
render() {
return (
<div className="StoryIndex theme-logs">
<Background src="https://wiki.aiterp.net/images/c/ce/Ehanis_Tioran_-_Portrait_-_Resized.png" />
<LogsMenu />
</div>
)
}
}
+17
View File
@@ -0,0 +1,17 @@
import React, { Component } from 'react';
import Menu, { MenuHeader, MenuLink, MenuGap } from '../../common/Menu';
export default class LogsMenu extends Component {
render() {
return (
<Menu>
<MenuHeader>Logs</MenuHeader>
<MenuLink to="/logs/" icon="R">Recent</MenuLink>
<MenuGap />
<MenuHeader>Options</MenuHeader>
<MenuLink icon="+">Create Log</MenuLink>
<MenuLink icon="I">Import Log</MenuLink>
</Menu>
)
}
}
+42
View File
@@ -0,0 +1,42 @@
import React, { Component } from "react";
import Menu, { MenuHeader, MenuLink, MenuGap } from "../../common/Menu";
import pluralize from "pluralize"
export default class StoryMenu extends Component {
render() {
return (
<Menu>
<MenuHeader>Story</MenuHeader>
<MenuLink to="/story/" icon="A">All</MenuLink>
<MenuGap />
<MenuHeader>By Category</MenuHeader>
<CategoryLinks categories={this.props.categories} />
<MenuGap />
<MenuHeader>By Tag</MenuHeader>
<MenuLink to="/story/tag/Location/Miner's%20Respite" icon="L" tagType="location">Miner's Respite</MenuLink>
<MenuLink to="/story/tag/Chracter/Ilyna%20T'Rea" icon="C" tagType="character">Ilyna T'Rea</MenuLink>
<MenuLink to="/story/tag/Organization/Redrock%20Agency" icon="O" tagType="organization">Redrock Agency</MenuLink>
<MenuLink to="/story/tag/Series/Yellow%20Haze" icon="S" tagType="series">Yellow Haze</MenuLink>
<MenuLink to="/story/tag/Event/Surveying%20The%20Ice%20World" icon="E" tagType="event">Surveying the Ice World</MenuLink>
<MenuLink to="/story/tag/" icon="T">All Tags</MenuLink>
<MenuGap />
<MenuHeader>Your Content</MenuHeader>
<MenuLink to="/story/authored/" icon="A">Listed</MenuLink>
<MenuLink to="/story/open/" icon="O">Open</MenuLink>
<MenuLink to="/story/unlisted/" icon="U">Unlisted</MenuLink>
<MenuGap />
<MenuHeader>Options</MenuHeader>
<MenuLink icon="+">Create Story</MenuLink>
</Menu>
)
}
}
function CategoryLinks({categories}) {
return categories.map(category => {
const icon = category.charAt(0).toUpperCase()
const label = pluralize(category)
return <MenuLink to={`/story/category/${category}`} icon={icon}>{label}</MenuLink>
})
}
+31
View File
@@ -0,0 +1,31 @@
import React, { Component } from 'react';
import { Query } from 'react-apollo';
import Background from '../../common/Background';
import StoryMenu from './StoryMenu';
import INDEX_QUERY from "./gql/StoryIndex";
export class StoryIndex extends Component {
render() {
const categories = this.props.categoryType.enumValues.map(ev => ev.name).sort()
return (
<div className="StoryIndex theme-story">
<Background />
<StoryMenu categories={categories} />
</div>
)
}
}
export default () => (
<Query query={INDEX_QUERY}>
{({ loading, error, data }) => {
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
return <StoryIndex {...data} />
}}
</Query>
);
@@ -0,0 +1,23 @@
import gql from "graphql-tag"
export default gql`
query StoryIndex {
categoryType: __type(name: "StoryCategory") {
enumValues {
name
description
}
}
stories(input: {}) {
id
name
author
open
tags {
kind
name
}
}
}
`