Skip to content
Snippets Groups Projects
Commit 000c8225 authored by Sebastian Martin Andresen's avatar Sebastian Martin Andresen
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
Pipeline #23414 failed
{
"plugins": [["@babel/plugin-proposal-class-properties", {"loose": true}]]
}
node_modules
.npmrc 0 → 100644
package-lock=false
{
"printWidth": 120,
"singleQuote": true
}
LICENSE 0 → 100644
MIT License
Copyright (c) 2018 tdat2003
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# React example with component state
## Prerequisites
- Flow and Prettier installed globally:
```sh
npm install -g flow-bin prettier
```
- Editor with Flow and prettier support
## Fetch, install dependencies, and run
```sh
git clone https://github.com/Sebsterr/Static-React
cd react-state
npm install
npm start
```
// flow-typed signature: 540e42745f797051f3bf17a6af1ccf06
// flow-typed version: 6a3fe49a8b/history_v4.x.x/flow_>=v0.25.x
declare module "history/createBrowserHistory" {
declare function Unblock(): void;
declare export type Action = "PUSH" | "REPLACE" | "POP";
declare export type BrowserLocation = {
pathname: string,
search: string,
hash: string,
// Browser and Memory specific
state: {},
key: string,
};
declare interface IBrowserHistory {
length: number,
location: BrowserLocation,
action: Action,
push(path: string, state?: {}): void,
push(location: $Shape<BrowserLocation>): void,
replace(path: string, state?: {}): void,
replace(location: $Shape<BrowserLocation>): void,
go(n: number): void,
goBack(): void,
goForward(): void,
listen: Function,
block(message: string): typeof Unblock,
block((location: BrowserLocation, action: Action) => string): typeof Unblock,
}
declare export type BrowserHistory = IBrowserHistory;
declare type HistoryOpts = {
basename?: string,
forceRefresh?: boolean,
getUserConfirmation?: (
message: string,
callback: (willContinue: boolean) => void,
) => void,
};
declare export default (opts?: HistoryOpts) => BrowserHistory;
}
declare module "history/createMemoryHistory" {
declare function Unblock(): void;
declare export type Action = "PUSH" | "REPLACE" | "POP";
declare export type MemoryLocation = {
pathname: string,
search: string,
hash: string,
// Browser and Memory specific
state: {},
key: string,
};
declare interface IMemoryHistory {
length: number,
location: MemoryLocation,
action: Action,
index: number,
entries: Array<string>,
push(path: string, state?: {}): void,
push(location: $Shape<MemoryLocation>): void,
replace(path: string, state?: {}): void,
replace(location: $Shape<MemoryLocation>): void,
go(n: number): void,
goBack(): void,
goForward(): void,
// Memory only
canGo(n: number): boolean,
listen: Function,
block(message: string): typeof Unblock,
block((location: MemoryLocation, action: Action) => string): typeof Unblock,
}
declare export type MemoryHistory = IMemoryHistory;
declare type HistoryOpts = {
initialEntries?: Array<string>,
initialIndex?: number,
keyLength?: number,
getUserConfirmation?: (
message: string,
callback: (willContinue: boolean) => void,
) => void,
};
declare export default (opts?: HistoryOpts) => MemoryHistory;
}
declare module "history/createHashHistory" {
declare function Unblock(): void;
declare export type Action = "PUSH" | "REPLACE" | "POP";
declare export type HashLocation = {
pathname: string,
search: string,
hash: string,
};
declare interface IHashHistory {
length: number,
location: HashLocation,
action: Action,
push(path: string, state?: {}): void,
push(location: $Shape<HashLocation>): void,
replace(path: string, state?: {}): void,
replace(location: $Shape<HashLocation>): void,
go(n: number): void,
goBack(): void,
goForward(): void,
listen: Function,
block(message: string): typeof Unblock,
block((location: HashLocation, action: Action) => string): typeof Unblock,
push(path: string): void,
}
declare export type HashHistory = IHashHistory;
declare type HistoryOpts = {
basename?: string,
hashType: "slash" | "noslash" | "hashbang",
getUserConfirmation?: (
message: string,
callback: (willContinue: boolean) => void,
) => void,
};
declare export default (opts?: HistoryOpts) => HashHistory;
}
// flow-typed signature: 53be1849af6037db65e90a7abc558afe
// flow-typed version: f4e99ca1ed/react-router-dom_v4.x.x/flow_>=v0.63.x
declare module "react-router-dom" {
import type { ComponentType, ElementConfig, Node, Component } from 'react';
declare export var BrowserRouter: Class<Component<{|
basename?: string,
forceRefresh?: boolean,
getUserConfirmation?: GetUserConfirmation,
keyLength?: number,
children?: Node
|}>>
declare export var HashRouter: Class<Component<{|
basename?: string,
getUserConfirmation?: GetUserConfirmation,
hashType?: "slash" | "noslash" | "hashbang",
children?: Node
|}>>
declare export var Link: Class<Component<{
className?: string,
to: string | LocationShape,
replace?: boolean,
children?: Node
}>>
declare export var NavLink: Class<Component<{
to: string | LocationShape,
activeClassName?: string,
className?: string,
activeStyle?: Object,
style?: Object,
isActive?: (match: Match, location: Location) => boolean,
children?: Node,
exact?: boolean,
strict?: boolean
}>>
// NOTE: Below are duplicated from react-router. If updating these, please
// update the react-router and react-router-native types as well.
declare export type Location = {
pathname: string,
search: string,
hash: string,
state?: any,
key?: string
};
declare export type LocationShape = {
pathname?: string,
search?: string,
hash?: string,
state?: any
};
declare export type HistoryAction = "PUSH" | "REPLACE" | "POP";
declare export type RouterHistory = {
length: number,
location: Location,
action: HistoryAction,
listen(
callback: (location: Location, action: HistoryAction) => void
): () => void,
push(path: string | LocationShape, state?: any): void,
replace(path: string | LocationShape, state?: any): void,
go(n: number): void,
goBack(): void,
goForward(): void,
canGo?: (n: number) => boolean,
block(
callback: (location: Location, action: HistoryAction) => boolean
): void,
// createMemoryHistory
index?: number,
entries?: Array<Location>
};
declare export type Match = {
params: { [key: string]: ?string },
isExact: boolean,
path: string,
url: string
};
declare export type ContextRouter = {|
history: RouterHistory,
location: Location,
match: Match,
staticContext?: StaticRouterContext
|};
declare type ContextRouterVoid = {
history: RouterHistory | void,
location: Location | void,
match: Match | void,
staticContext?: StaticRouterContext | void
};
declare export type GetUserConfirmation = (
message: string,
callback: (confirmed: boolean) => void
) => void;
declare export type StaticRouterContext = {
url?: string
};
declare export var StaticRouter: Class<Component<{|
basename?: string,
location?: string | Location,
context: StaticRouterContext,
children?: Node
|}>>
declare export var MemoryRouter: Class<Component<{|
initialEntries?: Array<LocationShape | string>,
initialIndex?: number,
getUserConfirmation?: GetUserConfirmation,
keyLength?: number,
children?: Node
|}>>
declare export var Router: Class<Component<{|
history: RouterHistory,
children?: Node
|}>>
declare export var Prompt: Class<Component<{|
message: string | ((location: Location) => string | boolean),
when?: boolean
|}>>
declare export var Redirect: Class<Component<{|
to: string | LocationShape,
push?: boolean,
from?: string,
exact?: boolean,
strict?: boolean
|}>>
declare export var Route: Class<Component<{|
component?: ComponentType<*>,
render?: (router: ContextRouter) => Node,
children?: ComponentType<ContextRouter> | Node,
path?: string,
exact?: boolean,
strict?: boolean,
location?: LocationShape,
sensitive?: boolean
|}>>
declare export var Switch: Class<Component<{|
children?: Node,
location?: Location
|}>>
declare export function withRouter<WrappedComponent: ComponentType<*>>(
Component: WrappedComponent
): ComponentType<
$Diff<ElementConfig<$Supertype<WrappedComponent>>, ContextRouterVoid>
>;
declare type MatchPathOptions = {
path?: string,
exact?: boolean,
sensitive?: boolean,
strict?: boolean
};
declare export function matchPath(
pathname: string,
options?: MatchPathOptions | string,
parent?: Match
): null | Match;
declare export function generatePath(pattern?: string, params?: Object): string;
}
{
"name": "react-state",
"version": "1.0.0",
"scripts": {
"postinstall": "node ./scripts/enable_babelrc.js",
"start": "react-scripts start"
},
"dependencies": {
"react-router-dom": "^4.3.1",
"react-scripts": "^2.0.4",
"react-simplified": "^1.5.1"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"devDependencies": {
"file-loader": "^2.0.0",
"url-loader": "^1.1.2"
}
}
This diff is collapsed.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>React example with static pages</title>
<link rel="stylesheet" href="bootstrap.min.css"/>
</head>
<body>
<div style=" margin-top: 30px;"/>
<div id="root"></div>
</body>
</html>
// Workaround for buggy transform of uninitialized class properties in react-scripts
const fs = require('fs');
const path = 'node_modules/react-scripts/config/webpack.config.dev.js';
fs.readFile(path, 'utf8', (error, data) => {
if (error) {
console.error(error);
process.exit(1);
}
let result = data.replace(/babelrc: false/g, 'babelrc: true');
fs.writeFile(path, result, 'utf8', error => {
if (error) {
console.error(error);
process.exit(1);
}
});
});
// @flow
/* eslint eqeqeq: "off" */
import ReactDOM from 'react-dom';
import * as React from 'react';
import { Component } from 'react-simplified';
import { HashRouter, Route, NavLink } from 'react-router-dom';
import { Alert, NavBar, Card, ListGroup, Button } from './widgets';
import createHashHistory from 'history/createHashHistory';
const history = createHashHistory(); // Use history.push(...) to programmatically change path, for instance after successfully saving a student
class Case {
id: number;
static nextId = 1;
overskrift: string;
content: string;
image: string;
comments: string[];
constructor(overskrift: string, content: string, image: string) {
this.id = Case.nextId++;
this.overskrift = overskrift;
this.content = content;
this.image = image;
this.comments = new Array(10);
}
comment(c: string) {
this.comments.push(c);
}
}
let cases = [
new Case('Title 1', 'Hei', 'http://allhdwallpapers.com/wp-content/uploads/2015/07/Storm-3.jpg'),
new Case('Title 2', '', 'http://apod.nasa.gov/apod/image/1704/ManDogSun_Hackmann_1600.jpg')
];
cases[0].comment('Veldig Bra');
cases[0].comment('xD');
class Menu extends Component {
render() {
return (
<NavBar>
<NavBar.Brand>Public Paper</NavBar.Brand>
<NavBar.Link to="/newCase">New Case 💘 </NavBar.Link>
<NavBar.Link to="/tags">Tags</NavBar.Link>
</NavBar>
);
}
}
class Home extends Component {
render() {
return (
<Card title=" ">
<ListGroup>
{cases.map(e => (
<Card title={e.overskrift}>
<ListGroup.Item>
<img class="card-img-top" src={e.image} alt={e.image} />
</ListGroup.Item>
<ListGroup.Item to={'/case/' + e.id}>Comments</ListGroup.Item>
</Card>
))}
</ListGroup>
</Card>
);
}
}
class CaseDetails extends Component<{ match: { params: { id: number } } }> {
render() {
let e = cases.find(e => e.id == this.props.match.params.id);
if (!e) {
Alert.danger('Case not found: ' + this.props.match.params.id);
return null; // Return empty object (nothing to render)
}
return (
<div>
<Card title={e.overskrift}>
<ListGroup.Item>
<img src={e.image} alt="Image Cap" />
</ListGroup.Item>
<ListGroup.Item>{e.content}</ListGroup.Item>
</Card>
<Card title="Comments">
<ListGroup>
{e.comments.map(comment => (
<ListGroup.Item>{comment}</ListGroup.Item>
))}
<ListGroup.Item>
<Button.Light onClick={this.comment}>Comment</Button.Light>
</ListGroup.Item>
</ListGroup>
</Card>
</div>
);
}
comment() {
history.push('/case/' + this.props.match.params.id + '/comment');
}
}
class newComment extends Component<{ match: { params: { id: number } } }> {
comment = '';
render() {
return (
<Card title="New Comment">
<form>
<ListGroup>
<ListGroup.Item>
<input
type="text"
value={this.comment}
onChange={(event: SyntheticInputEvent<HTMLInputElement>) => (this.comment = event.target.value)}
/>
</ListGroup.Item>
<ListGroup.Item>
<Button.Success onClick={this.save}>Comment</Button.Success>
<Button.Danger onClick={this.discard}>Discard</Button.Danger>
</ListGroup.Item>
</ListGroup>
</form>
</Card>
);
}
save() {
let e = cases.find(e => e.id == this.props.match.params.id);
if (!e) {
Alert.danger('Case not found: ' + this.props.match.params.id);
return null; // Return empty object (nothing to render)
}
e.comment(this.comment);
history.push('/case/' + this.props.match.params.id);
}
discard() {
history.push('/case/' + this.props.match.params.id);
}
}
class CasetAdd extends Component {
overskrift = '';
content = '';
image = '';
render() {
return (
<Card title="New Student">
<form>
<ListGroup>
<ListGroup.Item>
Title:{' '}
<input
type="text"
value={this.overskrift}
onChange={(event: SyntheticInputEvent<HTMLInputElement>) => (this.overskrift = event.target.value)}
/>
</ListGroup.Item>
<ListGroup.Item>
Image Link:{' '}
<input
type="text"
value={this.image}
onChange={(event: SyntheticInputEvent<HTMLInputElement>) => (this.image = event.target.value)}
/>
</ListGroup.Item>
<ListGroup.Item>
text:{' '}
<input
type="text"
value={this.content}
onChange={(event: SyntheticInputEvent<HTMLInputElement>) => (this.content = event.target.value)}
/>
</ListGroup.Item>
<ListGroup.Item>
<Button.Success onClick={this.save}>Register</Button.Success>
<Button.Danger onClick={this.discard}>Discard</Button.Danger>
</ListGroup.Item>
</ListGroup>
</form>
</Card>
);
}
save() {
let c = new Case(this.overskrift, this.content, this.image);
cases.push(c);
// Go to StudentDetails after successful save
history.push('/');
}
discard() {
history.push('/');
}
}
const root = document.getElementById('root');
if (root)
ReactDOM.render(
<HashRouter>
<div>
<Alert />
<Menu />
<Route exact path="/" component={Home} />
<Route exact path="/case/:id" component={CaseDetails} />
<Route exact path="/case/:id/comment" component={newComment} />
<Route exact path="/newCase" component={CasetAdd} />
</div>
</HashRouter>,
root
);
src/pp.jpg

72.6 KiB

// @flow
/* eslint eqeqeq: "off" */
import * as React from 'react';
import { Component } from 'react-simplified';
import { NavLink } from 'react-router-dom';
/**
* Renders alert messages using Bootstrap classes.
*/
export class Alert extends Component {
alerts: { text: React.Node, type: string }[] = [];
render() {
return this.alerts.map((alert, i) => (
<div key={i} className={'alert alert-' + alert.type} role="alert">
{alert.text}
<button
className="close"
onClick={() => {
this.alerts.splice(i, 1);
}}
>
&times;
</button>
</div>
));
}
static success(text: React.Node) {
// To avoid 'Cannot update during an existing state transition' errors, run after current event through setTimeout
setTimeout(() => {
for (let instance: Alert of Alert.instances()) instance.alerts.push({ text: text, type: 'success' });
});
}
static info(text: React.Node) {
// To avoid 'Cannot update during an existing state transition' errors, run after current event through setTimeout
setTimeout(() => {
for (let instance: Alert of Alert.instances()) instance.alerts.push({ text: text, type: 'info' });
});
}
static warning(text: React.Node) {
// To avoid 'Cannot update during an existing state transition' errors, run after current event through setTimeout
setTimeout(() => {
for (let instance: Alert of Alert.instances()) instance.alerts.push({ text: text, type: 'warning' });
});
}
static danger(text: React.Node) {
// To avoid 'Cannot update during an existing state transition' errors, run after current event through setTimeout
setTimeout(() => {
for (let instance: Alert of Alert.instances()) instance.alerts.push({ text: text, type: 'danger' });
});
}
}
export class Card extends Component<{ title: React.Node, children?: React.Node }> {
render() {
return (
<div className="card">
<div className="card-body">
<h5 className="card-title">{this.props.title}</h5>
<div className="card-text">{this.props.children}</div>
</div>
</div>
);
}
}
class ButtonSuccess extends Component<{
onClick: () => mixed, // Any function
children: React.Node
}> {
render() {
return (
<button className="btn btn-success" onClick={this.props.onClick}>
{this.props.children}
</button>
);
}
}
class ButtonDanger extends Component<{
onClick: () => mixed, // Any function
children: React.Node
}> {
render() {
return (
<button className="btn btn-danger" onClick={this.props.onClick}>
{this.props.children}
</button>
);
}
}
class ButtonLight extends Component<{
onClick: () => mixed, // Any function
children: React.Node
}> {
render() {
return (
<button className="btn btn-light" onClick={this.props.onClick}>
{this.props.children}
</button>
);
}
}
export class Button {
static Success = ButtonSuccess;
static Danger = ButtonDanger;
static Light = ButtonLight;
}
class ListGroupItem extends Component<{ to?: string, children: React.Node }> {
render() {
return this.props.to ? (
<NavLink className="list-group-item" activeClassName="active" to={this.props.to}>
{this.props.children}
</NavLink>
) : (
<li className="list-group-item">{this.props.children}</li>
);
}
}
/**
* Renders a list group using Bootstrap classes
*/
export class ListGroup extends Component<{
children: React.Element<typeof ListGroupItem> | (React.Element<typeof ListGroupItem> | null)[] | null
}> {
static Item = ListGroupItem;
render() {
return <ul className="list-group">{this.props.children}</ul>;
}
}
class NavBarBrand extends Component<{ children?: React.Node }> {
render() {
if (!this.props.children) return null;
return (
<NavLink className="navbar-brand" activeClassName="active" exact to="/">
{this.props.children}
</NavLink>
);
}
}
class NavBarLink extends Component<{ to: string, exact?: boolean, children?: React.Node }> {
render() {
if (!this.props.children) return null;
return (
<NavLink className="nav-link" activeClassName="active" exact={this.props.exact} to={this.props.to}>
{this.props.children}
</NavLink>
);
}
}
/**
* Renders a navigation bar using Bootstrap classes
*/
export class NavBar extends Component<{ children: React.Element<typeof NavBarBrand | typeof NavBarLink>[] }> {
static Brand = NavBarBrand;
static Link = NavBarLink;
render() {
return (
<nav className="navbar navbar-expand-sm fixed-top bg-light navbar-light">
{this.props.children.filter(child => child.type == NavBarBrand)}
<ul className="navbar-nav">{this.props.children.filter(child => child.type == NavBarLink)}</ul>
</nav>
);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment