initial styling

This commit is contained in:
Sebastian Sdorra
2018-07-11 14:59:01 +02:00
parent e3caa93aa7
commit d35a56e07e
18 changed files with 6742 additions and 116 deletions

View File

@@ -4,6 +4,7 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"bulma": "^0.7.1",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"history": "^4.7.2", "history": "^4.7.2",
"react": "^16.4.1", "react": "^16.4.1",
@@ -19,8 +20,12 @@
"redux-thunk": "^2.3.0" "redux-thunk": "^2.3.0"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/",
"build": "react-scripts build", "watch-css": "npm run build-css && node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/ --watch --recursive",
"start-js": "react-scripts start",
"start": "npm-run-all -p watch-css start-js",
"build-js": "react-scripts build",
"build": "npm-run-all build-css build-js",
"test": "jest", "test": "jest",
"eject": "react-scripts eject", "eject": "react-scripts eject",
"flow": "flow" "flow": "flow"
@@ -35,6 +40,8 @@
"enzyme-adapter-react-16": "^1.1.1", "enzyme-adapter-react-16": "^1.1.1",
"flow-bin": "^0.75.0", "flow-bin": "^0.75.0",
"flow-typed": "^2.5.1", "flow-typed": "^2.5.1",
"node-sass-chokidar": "^1.3.0",
"npm-run-all": "^4.1.3",
"prettier": "^1.13.7", "prettier": "^1.13.7",
"react-test-renderer": "^16.4.1" "react-test-renderer": "^16.4.1"
}, },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -19,7 +19,7 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>React App</title> <title>SCM-Manager</title>
</head> </head>
<body> <body>
<noscript> <noscript>

View File

@@ -1,6 +1,6 @@
{ {
"short_name": "React App", "short_name": "SCM-Manager",
"name": "Create React App Sample", "name": "SCM-Manager",
"icons": [ "icons": [
{ {
"src": "favicon.ico", "src": "favicon.ico",

View File

@@ -0,0 +1,31 @@
//@flow
import React from "react";
import Logo from "./Logo";
type Props = {
children?: React.Node
};
class Header extends React.Component<Props> {
render() {
const { children } = this.props;
return (
<section className="hero is-dark is-small">
<div className="hero-body">
<div className="container">
<div className="columns is-vcentered">
<div className="column">
<Logo />
</div>
</div>
</div>
</div>
<div className="hero-foot">
<div className="container">{children}</div>
</div>
</section>
);
}
}
export default Header;

View File

@@ -0,0 +1,49 @@
//@flow
import React from "react";
import Image from "../images/logo.png";
type Props = {
label?: string,
placeholder?: string,
type?: string,
onChange: string => void
};
class InputField extends React.Component<Props> {
static defaultProps = {
type: "text",
placeholder: ""
};
handleInput = (event: SyntheticInputEvent<HTMLInputElement>) => {
this.props.onChange(event.target.value);
};
renderLabel = () => {
const label = this.props.label;
if (label) {
return <label class="label">{label}</label>;
}
return "";
};
render() {
const { type, placeholder } = this.props;
return (
<div class="field">
{this.renderLabel()}
<div class="control">
<input
class="input"
type={type}
placeholder={placeholder}
onChange={this.handleInput}
/>
</div>
</div>
);
}
}
export default InputField;

View File

@@ -0,0 +1,11 @@
//@flow
import React from "react";
import Image from "../images/logo.png";
class Logo extends React.PureComponent {
render() {
return <img src={Image} alt="SCM-Manager logo" />;
}
}
export default Logo;

View File

@@ -0,0 +1,17 @@
//@flow
import React from "react";
import PrimaryNavigationLink from "./PrimaryNavigationLink";
class PrimaryNavigation extends React.Component<Props> {
render() {
return (
<nav className="tabs is-boxed">
<ul>
<PrimaryNavigationLink to="/users">Users</PrimaryNavigationLink>
</ul>
</nav>
);
}
}
export default PrimaryNavigation;

View File

@@ -0,0 +1,29 @@
//@flow
import React from "react";
import { Route, Link } from "react-router-dom";
type Props = {
to: string,
activeOnlyWhenExact?: boolean,
children?: React.Node
};
class PrimaryNavigationLink extends React.Component<Props> {
renderLink = (route: any) => {
const { to, children } = this.props;
return (
<li className={route.match ? "is-active" : ""}>
<Link to={to}>{children}</Link>
</li>
);
};
render() {
const { to, activeOnlyWhenExact } = this.props;
return (
<Route path={to} exact={activeOnlyWhenExact} children={this.renderLink} />
);
}
}
export default PrimaryNavigationLink;

View File

@@ -0,0 +1,38 @@
//@flow
import React from "react";
import Logo from "./Logo";
import classNames from "classnames";
type Props = {
value: string,
large?: boolean,
fullWidth?: boolean
};
class SubmitButton extends React.Component<Props> {
render() {
const { value, large, fullWidth } = this.props;
const largeClass = large ? "is-large" : "";
const fullWidthClass = fullWidth ? "is-fullwidth" : "";
return (
<div className="field">
<div className="control">
<input
type="submit"
className={classNames(
"button",
"is-link",
largeClass,
fullWidthClass
)}
value={value}
/>
</div>
</div>
);
}
}
export default SubmitButton;

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,14 @@
import React, { Component } from "react"; import React, { Component } from "react";
import Navigation from "./Navigation";
import Main from "./Main"; import Main from "./Main";
import Login from "./Login"; import Login from "./Login";
import { getIsAuthenticated } from "../modules/login"; import { getIsAuthenticated } from "../modules/login";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { withRouter } from "react-router-dom"; import { withRouter } from "react-router-dom";
import "./App.css";
import Header from "../components/Header";
import PrimaryNavigation from "../components/PrimaryNavigation";
type Props = { type Props = {
login: boolean, login: boolean,
username: string, username: string,
@@ -18,24 +21,26 @@ class App extends Component<Props> {
this.props.getAuthState(); this.props.getAuthState();
} }
render() { render() {
const { login, username, loading } = this.props; const { login, loading } = this.props;
let content;
let navigation;
if (loading) { if (loading) {
return <div>Loading...</div>; content = <div>Loading...</div>;
} else if (!login) { } else if (!login) {
return ( content = <Login />;
<div>
<Login />
</div>
);
} else { } else {
return ( content = <Main />;
<div className="App"> navigation = <PrimaryNavigation />;
<h2>Welcome, {username}!</h2>
<Navigation />
<Main />
</div>
);
} }
return (
<div className="App">
<Header>{navigation}</Header>
{content}
</div>
);
} }
} }

View File

@@ -0,0 +1,30 @@
@import "bulma/sass/utilities/initial-variables";
@import "bulma/sass/utilities/functions";
$blue: #33B2E8;
// $footer-background-color
.is-ellipsis-overflow {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.has-rounded-border {
border-radius: 0.25rem;
}
.is-full-width {
width: 100%;
}
.fitParent {
// TODO get rid of important
margin: 0 !important;
// 3.8em for line-numbers
padding: 0 0 0 3.8em !important;
}
// 6. Import the rest of Bulma
@import "bulma/bulma";

View File

@@ -4,15 +4,30 @@ import injectSheet from "react-jss";
import { login } from "../modules/login"; import { login } from "../modules/login";
import { connect } from "react-redux"; import { connect } from "react-redux";
import InputField from "../components/InputField";
import SubmitButton from "../components/SubmitButton";
import classNames from "classnames";
import Avatar from "../images/blib.jpg";
const styles = { const styles = {
wrapper: { spacing: {
width: "100%", paddingTop: "5rem"
display: "flex",
height: "10em"
}, },
login: { avatar: {
margin: "auto", marginTop: "-70px",
textAlign: "center" paddingBottom: "20px"
},
avatarImage: {
border: "1px solid lightgray",
padding: "5px",
background: "#fff",
borderRadius: "50%",
width: "128px",
height: "128px"
},
avatarSpacing: {
marginTop: "5rem"
} }
}; };
@@ -32,13 +47,13 @@ class Login extends React.Component<Props, State> {
this.state = { username: "", password: "" }; this.state = { username: "", password: "" };
} }
handleUsernameChange(event: SyntheticInputEvent<HTMLInputElement>) { handleUsernameChange = (value: string) => {
this.setState({ username: event.target.value }); this.setState({ username: value });
} };
handlePasswordChange(event: SyntheticInputEvent<HTMLInputElement>) { handlePasswordChange = (value: string) => {
this.setState({ password: event.target.value }); this.setState({ password: value });
} };
handleSubmit(event: Event) { handleSubmit(event: Event) {
event.preventDefault(); event.preventDefault();
@@ -48,24 +63,37 @@ class Login extends React.Component<Props, State> {
render() { render() {
const { classes } = this.props; const { classes } = this.props;
return ( return (
<div className={classes.wrapper}> <section className="hero is-fullheight">
<div className={classes.login}> <div className={classes.spacing}>
You need to log in! ... <div className="container has-text-centered">
<form onSubmit={this.handleSubmit.bind(this)}> <div className="column is-4 is-offset-4">
<input <h3 className="title">Login</h3>
type="text" <p className="subtitle">Please login to proceed.</p>
defaultValue="Username" <div className={classNames("box", classes.avatarSpacing)}>
onChange={this.handleUsernameChange.bind(this)} <figure className={classes.avatar}>
/> <img
<input className={classes.avatarImage}
type="password" src={Avatar}
defaultValue="Password" alt="SCM-Manager"
onChange={this.handlePasswordChange.bind(this)} />
/> </figure>
<input type="submit" value="Login" /> <form onSubmit={this.handleSubmit.bind(this)}>
</form> <InputField
placeholder="Your Username"
onChange={this.handleUsernameChange}
/>
<InputField
placeholder="Your Password"
type="password"
onChange={this.handlePasswordChange}
/>
<SubmitButton value="Login" fullWidth={true} />
</form>
</div>
</div>
</div>
</div> </div>
</div> </section>
); );
} }
} }

View File

@@ -1,38 +1,36 @@
//@flow //@flow
import React from 'react'; import React from "react";
import injectSheet from 'react-jss'; import injectSheet from "react-jss";
import classNames from 'classnames'; import classNames from "classnames";
import { Route, withRouter } from 'react-router'; import { Route, withRouter } from "react-router";
import Repositories from '../repositories/containers/Repositories'; import Repositories from "../repositories/containers/Repositories";
import Users from '../users/containers/Users'; import Users from "../users/containers/Users";
import {Switch} from 'react-router-dom'; import { Switch } from "react-router-dom";
const styles = { const styles = {
content: { content: {
paddingTop: '60px' paddingTop: "60px"
}, }
}; };
type Props = { type Props = {
classes: any classes: any
} };
class Main extends React.Component<Props> { class Main extends React.Component<Props> {
render() { render() {
const { classes } = this.props; const { classes } = this.props;
return ( return (
<div className={classNames('container', classes.content)}> <div className={classNames("container", classes.content)}>
<Switch> <Switch>
<Route exact path="/" component={Repositories} /> <Route exact path="/" component={Repositories} />
<Route exact path="/users" component={Users} /> <Route path="/users" component={Users} />
</Switch> </Switch>
</div> </div>
); );
} }
} }
export default withRouter(injectSheet(styles)(Main)); export default withRouter(injectSheet(styles)(Main));

View File

@@ -1,52 +0,0 @@
//@flow
import React from 'react';
import {Link} from 'react-router-dom';
type Props = {};
type State = {
collapsed: boolean
};
class Navigation extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
collapsed: true
};
}
toggleCollapse = () => {
this.setState({
collapsed: !this.state.collapsed
});
};
render() {
return (
<nav className="navbar navbar-default navbar-fixed-top">
<div className="container">
<div className="navbar-header">
<button type="button" className="navbar-toggle collapsed" data-toggle="collapse"
data-target="#navbar" aria-expanded="false" aria-controls="navbar"
onClick={this.toggleCollapse}>
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
</button>
<Link className="navbar-brand" to="/">
SCM 2 Test UI
</Link>
</div>
</div>
</nav>
);
}
}
export default Navigation;

BIN
scm-ui/src/images/blib.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
scm-ui/src/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB