Restructuring folders [ Broken ]

This commit is contained in:
2018-10-22 17:21:29 +11:00
parent 36b7b8629e
commit 776395520d
148 changed files with 238 additions and 198 deletions

View File

@ -0,0 +1,129 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
class ElementScrollFader extends React.Component {
constructor(props) {
super(props);
this.state = {
waiting: true,
visible: false
};
this.onScrollBound = this.onScroll.bind(this);
this.updateRectangleBound = this.updateRectangle.bind(this);
this.checkEffectBound = this.checkEffect.bind(this);
this.rect = null;
}
componentDidMount() {
//Update rectangle
if(this.rectTimer) clearInterval(this.rectTimer);
this.rectTimer = setInterval(this.updateRectangleBound, 100);
if(!this.initialCheckFunction) {
this.initialCheckFunction = setTimeout(this.checkEffectBound, 300);
}
document.addEventListener('scroll', this.onScrollBound, true);
document.addEventListener("DOMContentLoaded", this.updateRectangleBound);
}
componentWillUnmount() {
this.detachListener();
}
//Used for rect calculation
updateRectangle() {
if(!this.refs || !this.refs.fader) return;
this.rect = this.refs.fader.getBoundingClientRect();
}
onScroll(e) {
this.checkEffect();
}
//Common functions
detachListener() {
document.removeEventListener('scroll', this.onScrollBound);
document.removeEventListener("DOMContentLoaded", this.updateRectangleBound);
if(this.rectTimer) clearInterval(this.rectTimer);
if(this.initialCheckFunction) clearTimeout(this.initialCheckFunction);
}
checkEffect() {
if(typeof window === typeof undefined) return;
if(!this.refs || !this.refs.fader) return;
if(this.state.waiting) {
this.setState({
waiting: false
});
}
if(this.state.visible) return this.detachListener();
if(!this.rect) this.updateRectangle();
//Get bounds
var rect = this.rect;
//If our top is at least half way UP the page, show
if(rect.top > window.innerHeight / 1.5) return;
this.setState({
visible: true
});
this.detachListener();//stop Listening
if(this.props.onVisible) {
this.props.onVisible(this.refs.fader);
}
return true;
}
render() {
let clazz = "o-element-scroll-fader";
if(this.props.from) {
clazz += " from-" + this.props.from
} else {
clazz += " from-top";
}
if(this.state.visible || this.state.waiting) {
clazz += " is-visible";
}
if(this.props.className) clazz += " " + this.props.className;
return (
<div className={ clazz } ref="fader">
{ this.props.children}
</div>
);
}
}
export default ElementScrollFader;

View File

@ -0,0 +1,38 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
export default function(props) {
let style = props.style || "test";
let styleClassPrefix = "o-background--style-"+style;
let inners = [];
return (
<div className={"o-background "+styleClassPrefix}>
<div className={"o-background__inner " + styleClassPrefix + "__inner" }>
{ inners }
</div>
</div>
);
}

View File

@ -0,0 +1,37 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
export default (props) => {
let clazz = "o-content-box";
if(props.box) clazz += " is-box"
if(props.className) clazz += " " + props.className;
return (
<div className={clazz}>
{props.children}
</div>
);
};

View File

@ -0,0 +1,50 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
import ContentBox from './ContentBox';
export default function(props) {
let clazzes = "o-floating-content-box";
//Positions
let position = "middle center";
if(props.position) position = props.position;
clazzes += " " + position.split(" ").map(i => 'is-'+i).join(" ");
//Sizes`
let size = "medium";
if(props.size) size = props.size;
clazzes += " is-"+size;
//Custom Classes
if(props.className) clazzes += " " + props.className;
return (
<ContentBox {...props} className={ clazzes }>
<div className="o-floating-content-box__inner">
{ props.children }
</div>
</ContentBox>
);
}

View File

@ -0,0 +1,143 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
import LoadableImage from './LoadableImage';
export default class Image extends React.Component {
constructor(props) {
super(props);
}
onLoad(e) {
if(this.props.onLoad) this.props.onLoad(e);
}
onError() {
if(this.props.onError) this.props.onErorr(e);
}
render() {
if(this.props.loadable) {
return <LoadableImage {...this.props} />;
}
let sourceProps = Object.assign({}, this.props);
//Prop Manipulation
if(sourceProps.image) {
if(Array.isArray(sourceProps.image)) {
sourceProps.sources = sourceProps.image;
} else {
sourceProps.src = sourceProps.image;
}
}
if(sourceProps.src) {
if(sourceProps.src.images) sourceProps.sources = sourceProps.src.images;
if(sourceProps.src.width) sourceProps.width = sourceProps.src.width;
if(sourceProps.src.height) sourceProps.height = sourceProps.src.height;
}
//Image
let sourceElements = [];
let sources = {};
let defaultSrc = sourceProps.src;
let defaultAlt = sourceProps.alt;
let defaultWidth = sourceProps.width;
let defaultHeight = sourceProps.height;
if(sourceProps.sources) {
//Iterate over supplied sources
for(let i = 0; i < sourceProps.sources.length; i++) {
let x = sourceProps.sources[i];
let width = x.size || x.width;
let isLast = (i+1) === sourceProps.sources.length;
for(let scale = 1; scale <= 4; scale++) {
let scaledWidth = Math.round(width / scale);
let o = Object.assign({}, x);
o.scale = scale;
o.isLast = isLast;
sources[scaledWidth] = sources[scaledWidth] || [];
sources[scaledWidth].push(o);
}
}
let keys = Object.keys(sources);
keys.sort((l, r) => {
return parseInt(l) - parseInt(r);
});
let breakNext = false;
for(let i = 0; i < keys.length; i++) {
if(breakNext) break;
let k = keys[i];//The pixel size
let ss = sources[k];//Sources at this pixel resolution
let mediaQuery = '(max-width:'+k+'px)';
let sss = [];
let isNextBreak = false;
if(this.props.maxWidth && (i+1 < keys.length)) {
if(keys[i+1] > parseInt(this.props.maxWidth)) isNextBreak = true;
}
if(isNextBreak) {
breakNext = true;
mediaQuery = '(min-width:'+k+'px)';
}
if(ss.length && ss[0].isLast) {
let prev = i > 0 ? keys[i-1] : 0;
mediaQuery = '(min-width:'+prev+'px)';
}
for(let x = 0; x < ss.length; x++) {
let scale = ss[x];
let source = scale.src || scale.path;
sss.push( source + (scale.scale && scale.scale!=1 ? " "+scale.scale+"x" : "") );
}
sourceElements.push(
<source media={mediaQuery} srcSet={ sss.join(", ") } key={i} />
);
}
}
return (
<picture>
{ sourceElements }
<img
onLoad={ this.onLoad.bind(this) }
onError={ this.onError.bind(this) }
src={ defaultSrc }
alt={ defaultAlt }
className={ sourceProps.className }
width={ defaultWidth }
height={ defaultHeight }
title={sourceProps.title}
/>
</picture>
);
}
}

View File

@ -0,0 +1,81 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
import Image from './Image';
import Loader from './../loading/Loader';
import BoxSizer from './../layout/BoxSizer';
class LoadableImage extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true
};
}
componentDidMount() {}
componentWillUnmount() {}
onLoad() {
this.setState({
loading: false
});
}
onError() {
this.setState({
loading: false
});
}
render() {
let p = Object.assign({}, this.props);
p.loadable = false;
p.onLoad = this.onLoad.bind(this);
let image = <Image {...p} />;
let loader,imageSizerDuringLoad;
if(this.state.loading) {
loader = <Loader />;
if(p.width && p.height) {
imageSizerDuringLoad = <BoxSizer ratioWidth={p.width} ratioHeight={p.height} />
}
}
return (
<div className={"o-loadable-image " + (this.state.loading ? "is-loading" : "is-loaded")}>
{ loader }
{ imageSizerDuringLoad }
<div className="o-loadable-image__image-container">
{ image }
</div>
</div>
);
}
}
export default LoadableImage;

View File

@ -0,0 +1,185 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
import Button from './button/Button';
import ButtonGroup from './button/ButtonGroup';
import Form, { FormManager } from './form/Form';
import InputGroup from './group/InputGroup';
import Label from './label/Label';
import Keyboard from './../keyboard/Keyboard';
export default class Input extends React.Component {
constructor(props) {
super(props);
this.state = {
value: props.value,
focused: false
};
}
isFocused() {
return this.state && this.state.focused === true;
}
componentDidMount() {
if(this.props.manager) this.props.manager.addInput(this);
Keyboard.addListener(this);
}
componentWillUnmount() {
if(this.props.manager) this.props.manager.removeInput(this);
Keyboard.removeListener(this);
}
onKeyUp(e, k) {
if(!this.props.manager) return;
if(!this.isFocused()) return;
if(!k.isSubmit()) return;
this.props.manager.submit();
}
onChange(e, a, b) {
//Try my props
if(this.props.onChange && this.props.onChange(e) === false) return false;
//Try the form manager
if(this.props.manager && this.props.manager.onChange(this, e) === false) {
return false;
}
//Try something else?
this.setState({
value: e.target.value
});
}
onFocus() {
this.setState({ focused: true });
}
onBlur() {
this.setState({ focused: false });
}
render() {
let ElementType = "input";
let type = "text";
let value;
let clazzes = "o-input";
let innerClazzes = "o-input__inner";
let style;
let props = Object.assign({}, this.props);
//Determining
if(props.type) type = props.type;
//Values
if(props.value) {
value = props.value;
} else {
value = props.children;
}
//Style
if(props.style) {
style = props.style;
} else if(props.error || props.danger) {
style = "danger";
} else if(props.warning) {
style = "warning";
} else if(props.primary) {
style = "primary";
}
//Classes
clazzes += " is-"+type;
if(style) {
clazzes += " o-input--style-"+style;
innerClazzes += " o-input--style-"+style+"__inner";
}
if(props.className) {
clazzes += " " + props.className;
innerClazzes += " " + props.className + "-element";
}
//Clear junk props
delete props.manager;
//Now create the element
let element;
//First we need to switch things like submit and reset
if(type == "submit" || type == "reset" || type == "button") {
return (<Button
{...props}
className={props.className}
value={this.state.value}
/>);
} else if(type == "textarea") {
element = (<textarea
{...props}
className={innerClazzes}
onChange={this.onChange.bind(this)}
onFocus={this.onFocus.bind(this)}
onBlur={this.onBlur.bind(this)}
>{ this.state.value }</textarea>
);
} else {
element = (<ElementType
{...props}
onChange={this.onChange.bind(this)}
onFocus={this.onFocus.bind(this)}
onBlur={this.onBlur.bind(this)}
type={type}
value={ this.state.value }
className={innerClazzes}
/>);
}
return (
<div className={clazzes}>
{ element }
</div>
)
}
}
const TextArea = props => {
return <Input {...props} type="textarea" />
}
export {
Button,
ButtonGroup,
Form,
FormManager,
InputGroup,
TextArea,
Label
};

View File

@ -0,0 +1,104 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
import { NavLink } from 'react-router-dom';
export default class Button extends React.Component {
constructor(props) {
super(props);
}
render() {
let ElementType = "button";//Upper Camel-Case because of react requriements
let clazzes = "o-btn";
let type = "button";
let contents;
let href;
let to;
let activeClassName;
let style;
//Basic Element Determining
if(this.props.type) {
//Buttons and Input Buttons
type = this.props.type;
clazzes += " is-button";
} else if(this.props.href) {
//Anchor Tags1
ElementType = "a";
href = this.props.href;
clazzes += " is-link is-anchor";
} else if(this.props.to) {
//React NavLink/Link
to = this.props.to;
ElementType = NavLink;
clazzes += " is-link is-nav-link";
activeClassName = "is-active";
} else {
//Everything Else (button without a type);
clazzes += " is-not-button";
}
if(this.props.value) {
contents = this.props.value;
} else {
contents = this.props.children;
}
//Determine Style
if(this.props.primary) {
style = "primary"
} else if(this.props.secondary) {
style = "secondary";
} else if(this.props.danger) {
style = "danger";
} else if(this.props.warning) {
style = "warning";
} else if(this.props.style) {
style = this.props.style;
}
//Style Clazzes
if(style) {
clazzes += " o-btn--style-"+style;
}
//Determine extra clazzes
if(this.props.className) this.clazzes += " "+this.props.className;
return (
<ElementType
{...this.props}
type={type}
className={clazzes}
href={href}
to={to}
>
<span className={ "o-btn__inner" + (style ? " o-btn--style-" + style + "__inner" : "") }>
{contents}
</span>
</ElementType>
);
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
export default function(props) {
return (
<div {...props} className={"o-btn-group" + (props.className ? " "+props.className : "")}>
{props.children}
</div>
);
}

View File

@ -0,0 +1,239 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
import Loader, { LoaderBackdrop } from './../../loading/Loader';
import Input, { InputGroup, TextArea } from './../Input';
export default class Form extends React.Component {
constructor(props) {
super(props);
//Prepare our initial state
let s = {
ajax: props.ajax || false,
loader: props.loader || false,
loading: false,
onSubmit: props.onSubmit,
contentType: props.contentType || props.encType || "application/x-www-form-urlencoded",
manager: props.manager
};
//Determine action and method based off the internals
if(props.action) s.action = props.action;
if(props.method) s.method = props.method;
if(props.get) {
s.action = props.get;
s.method = "GET";
}
if(props.post) {
s.action = props.post;
s.method = "POST";
}
//Write our state to the component
this.state = s;
}
componentDidMount() {
if(this.props.manager) this.props.manager.addForm(this);
}
componentWillUnmount() {
if(this.props.manager) this.props.manager.removeForm(this);
}
submit() {
this.onSubmit();
}
onSubmit(e) {
//Is Ajax?
if(!this.state.ajax) {
return this.state.onSubmit ? this.state.onSubmit(e) : true;
}
if(e) e.preventDefault();
if(!this.state.action) return console.warning("This form has no action.");
if(this.state.submitting) return false;//Already submitting?
//Start submitting!
this.setState({
loading: true,
submitting: true
});
//Prepare our data
let data;
if(this.props.manager) {
data = this.props.manager.getFormData();
}
if(this.state.contentType == "application/json") {
let dataJson = {};
data.forEach(function(value, key) {
dataJson[key] = value;
});
data = JSON.stringify(dataJson);
}
//Prepare our request.
fetch(this.state.action, {
method: this.state.method,
mode: this.state.mode,
body: data,
headers: {
"Content-Type": this.state.contentType
}
})
.then(this.onSubmitted.bind(this))
.catch(this.onError.bind(this))
;
return false;
}
onSubmitted(response) {
let method = 'text';
let isJson = response.headers.get("Content-Type").toLowerCase().indexOf("application/json") !== -1;
if(isJson) method = 'json';
if(!response.ok) {
let is4xx = Math.floor(response.status / 400) === 1;
if(is4xx) {
return response[method]()
.then(this.onErrorText.bind(this))
.catch(this.onError.bind(this))
;
} else {
throw Error(response.statusText);
}
}
if(this.props.onData) return this.props.onData(response);
//Handle the old fashioned way (expect json)
response[method]()
.then(this.onData.bind(this))
.catch(this.onError.bind(this))
;
}
onData(response) {
if(this.props.onSuccess) return this.props.onSuccess(response);
console.log(response);
}
onErrorText(e,a,b) {
this.onError(e,a,b);
}
onError(e, a, b) {
this.setState({
loading: false,
submitting: false
});
if(this.props.onError) return this.props.onError(e, a, b);
if(e) console.error(e);
if(a) console.error(a);
if(b) console.error(b);
}
render() {
let clazz = "o-form";
if(this.props.className) clazz += " " + this.props.className;
//Do I need a loader?
let loader;
if(this.state.loader && this.state.loading) {
loader = (
<LoaderBackdrop className="o-form__loader">
<Loader className="o-form__loader-spinner" />
</LoaderBackdrop>
);
}
return (
<form
ref="formDOM"
className={clazz}
method={ this.state.method }
autoComplete={ this.props.autoComplete }
target={ this.props.target }
onSubmit={ this.onSubmit.bind(this) }
>
{ this.props.children }
{ loader }
</form>
);
}
}
//FormManager
class FormManager {
constructor() {
this.forms = [];
this.inputs = [];
}
addForm(form) { this.forms.push(form); }
addInput(input) { this.inputs.push(input); }
removeForm(form) {
let i = this.forms.indexOf(form);
if(i === -1) return;
this.forms.splice(i, 1);
}
removeInput(input) {
let i = this.inputs.indexOf(input);
if(i === -1) return;
this.inputs.splice(i, 1);
}
onChange(input, event) {}
submit() {
for(let i = 0; i < this.forms.length; i++) {
this.forms[i].submit();
}
}
getFormData() {
let data = new FormData();
for(let i = 0; i < this.inputs.length; i++) {
let input = this.inputs[i];
data.append(input.props.name, input.state.value);
}
return data;
}
}
export {
FormManager
};

View File

@ -0,0 +1,36 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
export default function(props) {
let clazzes = "o-form__group";
if(props.className) clazzes += " " + props.className;
return (
<div className={clazzes}>
{ props.children }
</div>
);
}

View File

@ -0,0 +1,35 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
export default function(props) {
let clazz = "o-label";
if(props.className) clazz += " " + props.className;
return (
<label htmlFor={ props.htmlFor } className={clazz}>
{ props.children }
</label>
);
}

View File

@ -0,0 +1,17 @@
import React from 'react';
export default (props) => {
let height = 100;/* Percentage of width */
//TODO: Add more methods of resizing this box.
if(props.ratioWidth && props.ratioHeight) {
height = 100 / props.ratioWidth * props.ratioHeight;
}
//Box Sizer
return (
<div className="o-box-sizer" style={{
paddingBottom: height + '%'
}} />
);
};

View File

@ -0,0 +1,53 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
const Loader = function(props) {
return (
<span className={"o-loader"+(props.className?" "+props.className:"")}>
<svg width="38" height="38" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" className="o-loader__image">
<g fill="none" fillRule="evenodd">
<g transform="translate(1 1)" strokeWidth="2">
<circle strokeOpacity=".5" cx="18" cy="18" r="18" />
<path id="test" d="M36 18c0-9.94-8.06-18-18-18">
</path>
</g>
</g>
</svg>
</span>
);
}
const LoaderBackdrop = function(props) {
return (
<div className={"o-loader__backdrop"+(props.className?" "+props.className:"")}>
{ props.children }
</div>
);
}
export default Loader;
export {
LoaderBackdrop
};

View File

@ -0,0 +1,154 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import { Button } from './../input/Input';
import Language from './../language/Language';
import { openModal, closeModal } from './../actions/ModalActions';
import { Heading4 } from './../typography/Typography';
import Keyboard from './../keyboard/Keyboard';
class Modal extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
Keyboard.addListener(this);
}
componentWillUnmount() {
Keyboard.removeListener(this);
}
onKeyUp(e) {
if(!Keyboard.isEscape()) return;
this.props.closeModal();
}
render() {
//Add necessary buttons
let buttons = [];
if(this.props.buttons) {
if(Array.isArray(buttons)) {
buttons.concat(this.props.buttons);
} else {
buttons.push(this.props.buttons);
}
}
if(this.props.close) {
buttons.push(<Button key="close" onClick={this.props.closeModal}>{ Language.get("modal.close") }</Button>);
}
//Inner divs
let heading,body,footer;
if(this.props.title) {
heading = (
<div className="o-modal__box-heading">
<Heading4 className="o-modal__title">
{ this.props.title }
</Heading4>
</div>
);
}
if(this.props.children) {
body = (
<div className="o-modal__box-body">
{ this.props.children }
</div>
);
}
if(buttons) {
footer = (
<div className="o-modal__box-footer">
{ buttons }
</div>
);
}
//Create our modal contents
let contents = (
<div className="o-modal">
<div className="o-modal__inner">
{/* Provides both a good overlay, and a nice clickable area */}
<div className="o-modal__backdrop" onClick={this.props.closeModal}>
</div>
{/* Box itself, has a background and a shadow */}
<div className={"o-modal__box" + (this.props.large ? " is-large":"")}>
{ heading }
{ body }
{ footer }
</div>
</div>
</div>
);
//Display?
let displayedContents = <div></div>;
if(this.props.modal.open) {
displayedContents = (
<CSSTransition
appear={true}
timeout={200}
classNames="o-modal__transition"
mountOnEnter={ true }
unmountOnExit={ true }
key="modal"
>
{ contents }
</CSSTransition>
);
}
//Wrap entire contents of modal in transitional container.
return (
<TransitionGroup className="o-modal__transition-container">
{ displayedContents }
</TransitionGroup>
);
}
}
const mapStateToProps = (state) => {
return {
modal: state.modal,
language: state.language
};
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
openModal: openModal,
closeModal: closeModal
},dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Modal);

View File

@ -0,0 +1,53 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
const Heading = (props) => {
let level = props.level || 1;
let CustomTag = "h"+level;
let clazz = "o-heading o-heading--"+level;
if(props.className) clazz += " " + props.className;
return (
<CustomTag {...props} className={clazz} />
);
}
export default Heading;
const Heading1 = (props) => { return <Heading {...props} level="1" />; };
const Heading2 = (props) => { return <Heading {...props} level="2" />; };
const Heading3 = (props) => { return <Heading {...props} level="3" />; };
const Heading4 = (props) => { return <Heading {...props} level="4" />; };
const Heading5 = (props) => { return <Heading {...props} level="5" />; };
const Heading6 = (props) => { return <Heading {...props} level="6" />; };
export {
Heading1,
Heading2,
Heading3,
Heading4,
Heading5,
Heading6
};

View File

@ -0,0 +1,31 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
export default (props) => {
let clazz = "o-paragraph";
if(props.className) clazz += " "+props.className;
return <p {...props} className={clazz} />
};

View File

@ -0,0 +1,32 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
export default function(props) {
return (
<p className={ "o-subtitle" + ( props.className ? " " + props.className : "") }>
{ props.children }
</p>
);
}

View File

@ -0,0 +1,32 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
export default function(props) {
return (
<h1 className={ "o-title" + ( props.className ? " " + props.className : "") }>
{ props.children }
</h1>
);
}

View File

@ -0,0 +1,41 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import Title from './Title';
import Subtitle from './Subtitle';
import Paragraph from './Paragraph';
import Heading, { Heading1, Heading2, Heading3, Heading4, Heading5, Heading6 } from './Heading';
export {
Title,
Subtitle,
Paragraph,
Heading,
Heading1,
Heading2,
Heading3,
Heading4,
Heading5,
Heading6
}

View File

@ -0,0 +1,164 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
import Loader from './../loading/Loader';
import Image from './../image/Image';
//Adjust the order to adjust the load position
const VALID_SOURCES = [
"webm",
"mp4",
"ogg"
]
export default class Video extends React.Component {
constructor(props) {
super(props);
//Initial State
this.state = {
autoPlay: this.props.autoPlay,
loop: this.props.loop,
loader: false,
controls: this.props.controls
};
//Bound events (for removing event listeners)
this.onPlayingBound = this.onPlaying.bind(this);
this.onWaitingBound = this.onWaiting.bind(this);
this.onPauseBound = this.onPause.bind(this);
this.onSeekedBound = this.onSeeked.bind(this);
this.onLoadStartBound = this.onLoadStart.bind(this);
}
componentDidMount() {
this.refs.video.addEventListener('playing', this.onPlayingBound);
this.refs.video.addEventListener('waiting', this.onWaitingBound);
this.refs.video.addEventListener('seeked', this.onSeekedBound);
this.refs.video.addEventListener('pause', this.onPauseBound);
this.refs.video.addEventListener('loadstart', this.onLoadStartBound);
}
componentWillUnmount() {
}
//Standard Events - https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Media_events
onPlaying() {
this.setState({
loader: false
});
}
onPause() {
this.setState({
loader: false
});
}
onSeeked() {
this.setState({
loader: false
});
}
onLoadStart() {
if(this.isPaused()) return;
this.setState({
loader: true
});
}
onWaiting() {
this.setState({
loader: true
});
}
//Media Highlevel Functions
isPaused() {
return this.refs.video.paused
}
//React Render
render() {
//TODO: Add state support, as well as functional controls.
//Sources
let sources = [];
let sourceProps = this.props.sources ? this.props.sources : this.props;
for(let i = 0; i < VALID_SOURCES.length; i++) {
let s = VALID_SOURCES[i];
if(!sourceProps[s]) continue;
sources.push(<source type={"video/"+s} src={sourceProps[s]} key={s} />);
}
//Classes
let clazz = "o-video";
if(this.props.className) clazz += " " + this.props.className;
if(sourceProps.image) clazz += " has-image";
if(sourceProps.gif) clazz += " has-gif";
if(this.state.autoplay) clazz += " is-autoplaying";
if(this.state.loop) clazz += " is-looping";
let videoClass = "o-video__video";
if(this.props.fill) videoClass += " is-full";
//Fallback.
let fallback;
if(sourceProps.image) {
fallback = <Image image={sourceProps.image} alt={sourceProps.alt} className="o-video__image" />
}
//Loader
let loader;
if(this.state.loader) {
loader = <Loader className="o-video__loader" />
}
return (
<div className={clazz}>
{ /* Video Element (And sources) */ }
<video
className={ videoClass }
autoPlay={this.state.autoPlay}
loop={this.state.loop}
controls={this.state.controls}
ref="video"
>
{ sources }
</video>
{/* Loader */}
{ loader }
{ /* Fallback Picture */ }
{ fallback }
</div>
);
}
}

View File

@ -0,0 +1,65 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
import { connect } from 'react-redux';
import Frame from './Frame';
import Language from './../language/Language';
class AddressBar extends React.Component {
constructor(props) {
super(props);
}
onChange() {
}
render() {
let clazz = "o-window__address-bar";
return (
<div className={clazz}>
<span className="o-window__address-bar-title">
{ Language.get("window.address") }
</span>
<Frame className="o-window__address-bar-frame">
<input
type="text"
value={this.props.href}
className="o-window__input o-window__address-bar-input"
onChange={ this.onChange.bind(this) }
/>
</Frame>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
code: state.language.code
};
}
export default connect(mapStateToProps)(AddressBar);

View File

@ -0,0 +1,53 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
class ContextMenu extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="o-window__context-menu" ref="menu">
{ this.props.children }
</div>
);
}
}
export default ContextMenu;
const ContextMenuOption = (props) => {
return (
<div className="o-window__context-menu-option">
<div className="o-window__context-menu-option-title">
{ props.title }
</div>
</div>
);
};
export {
ContextMenuOption
}

View File

@ -0,0 +1,37 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
export default (props) => {
let clazz = "o-window__frame";
if(props.className) {
clazz += " " + props.className;
}
return (
<div className={clazz}>
{ props.children }
</div>
);
}

View File

@ -0,0 +1,143 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
import ContextMenu from './ContextMenu';
import { Link } from 'react-router-dom';
export default (props) => {
return (
<div className="o-window__menu-bar">
{ props.children }
</div>
);
}
class MenuOption extends React.Component {
constructor(props) {
super(props);
//Bound Methods
this.onClickBound = this.onClick.bind(this);
this.onMouseLeaveBound = this.onMouseLeave.bind(this);
this.state = {
open: false,
disabled: props.disabled || false
}
}
hasMenu() {
return this.props.menu;
}
isMenuOpen() { return this.state.open; }
isDisabled() { return this.state.disabled; }
//Event Handlers
onClick() {
if(this.isDisabled()) return;
if(this.hasMenu()) {
this.setState({
open: !this.state.open
});
}
}
onMouseLeave() {
if(!this.hasMenu()) return;
if(!this.isMenuOpen()) return;
this.setState({
open: false
});
}
componentDidMount() {
//Add event listeners
this.refs.button.addEventListener('click', this.onClickBound);
this.refs.option.addEventListener('mouseleave', this.onMouseLeaveBound);
}
componentWillUnmount() {
//Remove event listeners
this.refs.button.removeEventListener('click', this.onClickBound);
this.refs.option.removeEventListener('mouseleave', this.onMouseLeaveBound);
}
//Render method
render() {
let menu;
let button;
if(this.props.menu) {
menu = (
<ContextMenu>
{ this.props.menu }
</ContextMenu>
);
clazz += " has-menu";
}
if(this.props.href) {
button = (
<a href={this.props.href} className="o-window__menu-bar-button" ref="button" target={this.props.target}>
{ this.props.title }
</a>
);
} else if(this.props.to) {
button = (
<Link to={this.props.to} className="o-window__menu-bar-button" ref="button" target={this.props.target}>
{ this.props.title }
</Link>
);
} else {
button = (
<div className="o-window__menu-bar-button" ref="button">
{this.props.title}
</div>
);
}
let clazz = "o-window__menu-bar-option"
if(this.props.disabled) {
clazz += " is-disabled";
}
if(this.state.open) {
clazz += " is-active";
}
return (
<div className={clazz} ref="option">
{ button }
{ menu }
</div>
);
}
};
export {
MenuOption
};

View File

@ -0,0 +1,69 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
//Title Bar
export default (props) => {
return (
<div className="o-window__title-bar">
<div className="o-window__title-bar-buttons">
{ props.buttons }
</div>
<div className="o-window__title-bar-title">
{ props.children }
</div>
</div>
);
}
//Buttons
const TitleBarButton = (props) => {
let clz = "o-window__title-bar-button";
if(props.className) clz += " o-window__title-bar-button--"+props.className;
if(props.disabled) clz += " is-disabled";
return (
<div className={clz}>
{ props.children }
</div>
);
}
const Close = (props) => {
return (
<TitleBarButton {...props} className="close" title="Close" />
);
}
const Minimize = (props) => {
return (
<TitleBarButton {...props} className="minimize" title="Minimize" />
);
}
export {
TitleBarButton,
Close,
Minimize
}

View File

@ -0,0 +1,53 @@
// Copyright (c) 2018 Dominic Masters
//
// MIT License
//
// 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.
import React from 'react';
import TitleBar, { Close, Minimize } from './TitleBar';
import MenuBar, { MenuOption } from './MenuBar';
import ContextMenu, { ContextMenuOption } from './ContextMenu';
import Frame from './Frame';
import AddressBar from './AddressBar';
export default (props) => {
return (
<div className="o-window">
{ props.children }
</div>
);
}
export {
TitleBar,
Close,
Minimize,
MenuBar,
MenuOption,
ContextMenu,
ContextMenuOption,
Frame,
AddressBar
}