first commit

master
eater 4 years ago
commit 7918628baa
Signed by: eater
GPG Key ID: AD2560A0F84F0759

3
.gitignore vendored

@ -0,0 +1,3 @@
/dist
/.cache
/node_modules

@ -0,0 +1,82 @@
@import "vars";
.lore-editor {
display: flex;
flex-direction: column;
.title > input {
background-color: var(--color-background-light);
color: var(--color-foreground);
border: none;
font-size: 1.4em;
padding: 0.3em;
width: 100%;
}
.bar {
border-bottom: 1px solid var(--color-accent);
display: flex;
flex-wrap: wrap;
margin-left: -1px;
padding-bottom: 0.3em;
padding-top: 0.3em;
.group {
padding: 0.2em;
grid-row-gap: 0.3em;
display: inline-flex;
border-collapse: collapse;
border-left: 1px solid var(--color-accent);
span {
border-radius: 0.2em;
padding: 0.3em;
cursor: pointer;
&:hover, &.active {
color: var(--color-accent-alt);
}
}
}
}
.container {
padding: 1em;
background: var(--color-background-light);
overflow: auto;
flex-grow: 2;
.DraftEditor-root {
height: 100%;
div[data-contents=true] {
& > *:first-child {
margin-top: 0;
}
h1, h2, h3, h4, h5 {
& > div:before {
content: "» ";
display: inline;
}
}
pre {
background-color: lighten($background-light, 30%);
font-size: 1.1em;
padding: 1em;
}
pre > pre {
padding: 0;
margin: 0;
}
blockquote {
border-left: 3px solid lighten($background-light, 30%);
padding-left: 0.5em;
}
}
}
}
}

@ -0,0 +1,10 @@
$background-light: #333533;
:root {
// https://coolors.co/434371-e8eddf-79aea3-242423-333533
--color-background: #242423;
--color-foreground: #E8EDDF;
--color-accent: #434371;
--color-accent-alt: #79AEA3;
--color-background-light: #333533;
}

@ -0,0 +1,59 @@
@import "vars";
@import "lore-editor";
#app, body, html {
height: 100%;
margin: 0;
padding: 0;
font-family: sans-serif;
background: var(--color-background);
color: var(--color-foreground);
}
* {
box-sizing: border-box;
}
#app > * {
height: 100%;
}
.dock {
display: grid;
grid-template-columns: max(300px, 10%) auto max(250px, 10%);
grid-template-rows: 40px auto 20px;
grid-template-areas: "menu menu menu" "tree main properties" "status status status";
}
.dock > * > * {
width: 100%;
height: 100%;
}
.dock--main, .dock--tree, .dock--properties {
overflow: auto;
}
.dock--status, .dock--menu {
overflow: hidden;
}
.dock--menu {
grid-area: menu;
}
.dock--tree {
grid-area: tree;
}
.dock--main {
grid-area: main;
}
.dock--properties {
grid-area: properties;
}
.dock--status {
grid-area: status;
}

@ -0,0 +1,31 @@
{
"name": "schepper",
"version": "1.0.0",
"description": "Lore-management",
"author": "eater <=@eater.me>",
"license": "GPL3-or-later",
"devDependencies": {
"@babel/core": "^7.9.0",
"@babel/preset-react": "^7.9.4",
"parcel-bundler": "^1.12.4",
"sass": "^1.26.5"
},
"scripts": {
"dev": "parcel public/index.html",
"build": "parcel build public/index.html"
},
"babel": {
"presets": [
"@babel/preset-react"
]
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.28",
"@fortawesome/free-solid-svg-icons": "^5.13.0",
"@fortawesome/react-fontawesome": "^0.1.9",
"draft-js": "^0.11.5",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"@eater/react-treebeard": "^3.2.4"
}
}

@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Schepper</title>
</head>
<body>
<div id="app"></div>
<script src="../src/index.js"></script>
</body>
</html>

@ -0,0 +1,19 @@
import React, {Component} from 'react'
import LoreEditor from "./component/LoreEditor";
import LoreTree from "./component/LoreTree";
export default class App extends Component {
render(props) {
return <div className="dock">
<div className="dock--menu"/>
<div className="dock--tree">
<LoreTree />
</div>
<div className="dock--main">
<LoreEditor/>
</div>
<div className="dock--properties"/>
<div className="dock--status"/>
</div>
}
}

@ -0,0 +1,96 @@
import React, {Component} from "react";
import {Editor, EditorState, RichUtils} from "draft-js";
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
import * as fa from "@fortawesome/free-solid-svg-icons"
import "draft-js/dist/Draft.css"
export default class LoreEditor extends Component {
constructor(props) {
super(props);
this.state = {editorState: EditorState.createEmpty()};
this.onChange = editorState => {
this.setState({editorState});
}
this.handleKeyCommand = this.handleKeyCommand.bind(this);
}
handleKeyCommand(command, editorState) {
// Allows the use of key binds like Ctrl + B
const newState = RichUtils.handleKeyCommand(editorState, command);
if (newState) {
this.onChange(newState);
return 'handled';
}
return 'not-handled';
}
blockTypeButton(name, blockType) {
const selection = this.state.editorState.getSelection();
const currentBlockType = this.state.editorState
.getCurrentContent()
.getBlockForKey(selection.getStartKey())
.getType();
return <span className={currentBlockType === blockType ? "active" : ""}
onMouseDown={(e) => {
// Don't do system mouse down event,
// because we'll be unfocused from the text editor
e.preventDefault()
this.onChange(RichUtils.toggleBlockType(this.state.editorState, blockType))
}}>{name}</span>
}
styleButton(name, style) {
const currentStyle = this.state.editorState.getCurrentInlineStyle();
return <span className={currentStyle.contains(style) ? "active" : ""}
onMouseDown={(e) => {
// Don't do system mouse down event,
// because we'll be unfocused from the text editor
e.preventDefault()
this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, style))
}}>{name}</span>
}
render() {
return (
<div className="lore-editor">
<div className="title">
<input type="text" value={this.state.title} onChange={() => this.setState({title: this.titleInput.value})}
ref={el => this.titleInput = el}/>
</div>
<div className="bar">
<span className="group">
{this.blockTypeButton("H1", "header-one")}
{this.blockTypeButton("H2", "header-two")}
{this.blockTypeButton("H3", "header-three")}
{this.blockTypeButton("H4", "header-four")}
{this.blockTypeButton("H5", "header-five")}
{this.blockTypeButton(<FontAwesomeIcon title="Block quote" icon={fa.faIndent}/>, "blockquote")}
{this.blockTypeButton(<FontAwesomeIcon title="Unordered list" icon={fa.faListUl}/>, "unordered-list-item")}
{this.blockTypeButton(<FontAwesomeIcon title="Ordered list" icon={fa.faListOl}/>, "ordered-list-item")}
{this.blockTypeButton(<FontAwesomeIcon title="Code block" icon={fa.faCode}/>, "code-block")}
</span>
<span className="group">
{this.styleButton(<FontAwesomeIcon title="Bold" icon={fa.faBold}/>, "BOLD")}
{this.styleButton(<FontAwesomeIcon title="Italic" icon={fa.faItalic}/>, "ITALIC")}
{this.styleButton(<FontAwesomeIcon title="Underline" icon={fa.faUnderline}/>, "UNDERLINE")}
{this.styleButton(<FontAwesomeIcon title="Strike-through" icon={fa.faStrikethrough}/>, "STRIKETHROUGH")}
{this.styleButton(<FontAwesomeIcon title="Monospace" icon={fa.faTextWidth}/>, "CODE")}
</span>
</div>
<div className="container">
<Editor
editorState={this.state.editorState}
handleKeyCommand={this.handleKeyCommand}
onChange={this.onChange}
/>
</div>
</div>
);
}
}

@ -0,0 +1,80 @@
import React, {PureComponent} from "react";
import {Treebeard} from "@eater/react-treebeard";
import style from "../style";
import Toggle from "./LoreTree/Toggle";
import Header from "./LoreTree/Header";
const decorators = {
Loading: (props) => {
return (
<div style={props.style}>
loading...
</div>
);
},
Toggle,
Header,
Container: (props) => {
return (
<div style={{
paddingLeft: props.depth * (props.style.toggle.width + 6),
backgroundColor: (props.node.active ? style.colorBackgroundLight : null)
}}
onClick={props.onSelect} onDoubleClick={props.onClick}>
<props.decorators.Toggle onClick={props.onClick} style={props.style} terminal={props.terminal}
node={props.node}/>
<props.decorators.Header style={props.style} terminal={props.terminal} node={props.node}/>
</div>
);
}
};
export default class LoreTree extends PureComponent {
constructor(props) {
super(props);
this.state = {
// Random data to test for now
data: [{
name: 'Lore',
children: [
{
name: 'Events',
children: [
{name: 'Battle of Heck'},
{name: 'Treaty Of Fuck'}
]
}
]
}]
}
}
onToggle(node, toggled) {
const {data} = this.state;
if (node.children) {
node.toggled = toggled;
}
this.setState(() => ({data: [].concat(data)}));
}
onSelect(node) {
const {cursor, data} = this.state;
// `node` is already our cursor, quick return
if (cursor === node) {
return;
}
if (cursor) {
cursor.active = false;
}
node.active = true;
this.setState(() => ({cursor: node, data: [].concat(data)}));
}
render() {
return (<Treebeard data={this.state.data} style={style.theme.treebeard} decorators={decorators}
onToggle={this.onToggle.bind(this)} onSelect={this.onSelect.bind(this)}/>)
}
}

@ -0,0 +1,17 @@
import React, {PureComponent} from "react";
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
import * as fa from "@fortawesome/free-solid-svg-icons"
export default class Header extends PureComponent {
render() {
const style = this.props.style.header;
return <span style={style.base}>
<span style={style.title}>
<span style={style.icon}>{this.props.node.icon ||
<FontAwesomeIcon icon={this.props.terminal ? fa.faFile : fa.faFolder}/>}</span>
{this.props.node.name}
</span>
</span>
}
}

@ -0,0 +1,16 @@
import React, {PureComponent} from "react";
import style from "../../style";
export default class Toggle extends PureComponent {
render() {
return <span onClick={this.props.onClick} style={this.props.style.toggle.base}>
<span style={this.props.style.toggle.wrapper}>
<svg width={this.props.style.toggle.width} height={this.props.style.toggle.height} viewBox="-1 -1 12 12">
{this.props.terminal ? null :
<polygon points={this.props.node.toggled ? "0,0 10,0 5,10" : "0,0 10,5 0,10"} fill={style.colorAccent}/>
}
</svg>
</span>
</span>
}
}

@ -0,0 +1,7 @@
import React from "react"
import {render} from "react-dom";
import App from "./App";
import "../css/main.scss";
render(<App/>, document.getElementById("app"));

@ -0,0 +1,81 @@
// https://coolors.co/434371-e8eddf-79aea3-242423-333533
const c = {
colorBackground: '#242423',
colorForeground: '#E8EDDF',
colorAccent: '#434371',
colorAccentAlt: '#79AEA3',
colorBackgroundLight: '#333533',
}
export default {
...c,
theme: {
treebeard: {
tree: {
base: {
backgroundColor: c.colorBackground
},
node: {
base: {
color: c.colorForeground
},
activeLink: {
color: c.colorAccentAlt
},
toggle: {
base: {
position: 'relative',
display: 'inline-block',
verticalAlign: 'top',
height: '24px',
width: '24px'
},
wrapper: {
position: 'absolute',
top: '50%',
left: '50%',
margin: '-7px 0 0 -7px',
height: '14px'
},
height: 14,
width: 14,
arrow: {
fill: '#9DA5AB',
strokeWidth: 0
}
},
header: {
base: {
display: 'inline-block',
verticalAlign: 'top',
userSelect: 'none',
},
icon: {
width: '20px',
fontSize: '15px',
display: 'inline-block',
color: c.colorAccentAlt,
},
connector: {
width: '2px',
height: '12px',
borderLeft: 'solid 2px black',
borderBottom: 'solid 2px black',
position: 'absolute',
top: '0px',
left: '-21px'
},
title: {
lineHeight: '24px',
verticalAlign: 'middle'
}
},
subtree: {
listStyle: 'none',
paddingLeft: 0,
},
}
}
}
}
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save