This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
.App {
|
||||
|
||||
}
|
||||
+12
-3
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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} />
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -2,4 +2,10 @@ body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: sans-serif;
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
+19
-6
@@ -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();
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
Reference in New Issue
Block a user