Add animation, multiple alerts, make them float

This commit is contained in:
Luka Murn 2017-09-28 19:39:17 +02:00
parent f6c7e854ad
commit 6951755a92
7 changed files with 167 additions and 48 deletions

View file

@ -1,8 +1,13 @@
import React, { Component } from "react";
import styled from "styled-components";
import PropTypes from "prop-types";
import { Grid, Row, Col } from "react-bootstrap";
const Wrapper = styled.div`
margin-bottom: 0;
`;
class Alert extends Component {
static alertClass(type) {
const classes = {
error: "alert-danger",
@ -35,20 +40,18 @@ class Alert extends Component {
}
render() {
const message = this.props.message;
const alertClassName =
`alert
${Alert.alertClass(message.type)}
${Alert.alertClass(this.props.type)}
alert-dismissable
alert-floating
fade in`;
alert-floating`;
const glyphiconClassName =
`glyphicon
${Alert.glyphiconClass(message.type)}`;
${Alert.glyphiconClass(this.props.type)}`;
return(
<div className={alertClassName}>
<div className="container">
<Wrapper className={alertClassName}>
<Grid><Row><Col>
<button type="button"
className="close"
data-dismiss="alert"
@ -58,25 +61,23 @@ class Alert extends Component {
<span aria-hidden="true">×</span>
</button>
<span className={glyphiconClassName} />
<span>&nbsp;{message.text}</span>
</div>
</div>
<span>&nbsp;{this.props.message}</span>
</Col></Row></Grid>
</Wrapper>
);
}
}
Alert.propTypes = {
onClose: PropTypes.func,
message: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
timeout: PropTypes.number,
message: PropTypes.shape({
type: PropTypes.string.isRequired,
text: PropTypes.string.isRequired
}).isRequired
onClose: PropTypes.func
};
Alert.defaultProps = {
onClose: null,
timeout: 3000
timeout: 5000,
onClose: undefined
};
export default Alert;

View file

@ -0,0 +1,79 @@
import React, { Component } from "react";
import styled from "styled-components";
import update from "immutability-helper";
import TransitionGroup from 'react-transition-group/TransitionGroup';
import CSSTransition from 'react-transition-group/CSSTransition';
import Alert from "./components/Alert";
const Wrapper = styled.div`
position: absolute;
z-index: 1000;
width: 100%;
`;
class AlertsContainer extends Component {
constructor(props) {
super(props);
this.state = {
alerts: []
};
this.add = this.add.bind(this);
this.clearAll = this.clearAll.bind(this);
this.renderAlert = this.renderAlert.bind(this);
// Bind self to global namespace
window.alerts = this;
}
add(message, type, timeout) {
this.setState(
update(
this.state,
{
alerts: { $push: [{ message, type, timeout }] }
}
)
);
}
clearAll() {
this.setState({ alerts: [] });
}
clear(alert) {
const index = this.state.alerts.indexOf(alert);
const alerts = update(this.state.alerts, { $splice: [[index, 1]] });
this.setState({ alerts: alerts });
}
renderAlert(alert) {
return (
<Alert message={alert.message}
type={alert.type}
timeout={alert.timeout}
onClose={() => this.clear(alert)}
/>
);
}
render() {
return (
<Wrapper>
<TransitionGroup>
{this.state.alerts.map((alert, index) =>
<CSSTransition key={`alert-${index}`}
timeout={500}
classNames="alert-animated"
>
{this.renderAlert(alert, index)}
</CSSTransition>
)}
</TransitionGroup>
</Wrapper>
);
}
}
export default AlertsContainer;

View file

@ -1,14 +1,15 @@
import React, { Component } from "react";
import React from "react";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { IntlProvider, addLocaleData } from "react-intl";
import enLocaleData from "react-intl/locale-data/en";
import styled from "styled-components";
import { flattenMessages } from "./config/locales/utils";
import messages from "./config/locales/messages";
import store from "./config/store";
import Spinner from "./components/Spinner";
import Alert from "./components/Alert";
import AlertsContainer from "./components/AlertsContainer";
import ModalsContainer from "./components/ModalsContainer";
import SettingsPage from "./scenes/SettingsPage";
import Navigation from "./components/Navigation";
@ -16,36 +17,30 @@ import Navigation from "./components/Navigation";
addLocaleData([...enLocaleData]);
const locale = "en-US";
class ScinoteApp extends Component {
constructor(props) {
super(props);
const a = 5;
}
const ContentWrapper = styled.div`
margin-top: 15px;
`;
render() {
return (
<Provider store={store}>
<IntlProvider locale={locale}
messages={flattenMessages(messages[locale])}
>
const ScinoteApp = () =>
<Provider store={store}>
<IntlProvider locale={locale}
messages={flattenMessages(messages[locale])}
>
<div>
<BrowserRouter>
<div>
<BrowserRouter>
<div>
<Navigation />
<div>
<Alert message={{type: "alert", text: "Yadayadayada!!!"}} />
</div>
<SettingsPage />
</div>
</BrowserRouter>
<ModalsContainer />
<Spinner />
<Navigation />
<AlertsContainer />
<ContentWrapper>
<SettingsPage />
</ContentWrapper>
</div>
</IntlProvider>
</Provider>
);
}
}
</BrowserRouter>
<ModalsContainer />
<Spinner />
</div>
</IntlProvider>
</Provider>;
export default ScinoteApp;

View file

@ -0,0 +1,27 @@
.alert-animated-enter {
opacity: 0.01;
}
.alert-animated-enter.alert-animated-enter-active {
opacity: 1;
transition: opacity 150ms ease-in;
}
.alert-animated-exit {
opacity: 1;
padding-bottom: 15px;
padding-top: 15px;
margin-bottom: 20px;
height: 50px;
}
.alert-animated-exit.alert-animated-exit-active {
overflow: hidden;
opacity: 0.01;
padding-top: 0px;
padding-bottom: 0px;
margin-bottom: 0px;
height: 0px;
transition: opacity 300ms ease-in,
padding-top 500ms ease-in,
padding-bottom 500ms ease-in,
margin-bottom 500ms ease-in,
height 500ms ease-in;
}

View file

@ -1,4 +1,5 @@
@import 'constants';
@import 'animations';
@import 'react-bootstrap-timezone-picker/dist/react-bootstrap-timezone-picker.min.css';
@import '~react-bootstrap-table/dist/react-bootstrap-table.min';
@import 'react-tagsinput/react-tagsinput.css';

View file

@ -69,6 +69,7 @@
"react-bootstrap-timezone-picker": "^1.0.11",
"react-data-grid": "^2.0.2",
"react-tagsinput": "^3.17.0",
"react-transition-group": "^2.2.0",
"react-dom": "^15.6.1",
"react-intl": "^2.3.0",
"react-intl-redux": "^0.6.0",

View file

@ -1156,6 +1156,10 @@ center-align@^0.1.1:
align-text "^0.1.3"
lazy-cache "^1.0.3"
chain-function@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc"
chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@ -4825,6 +4829,17 @@ react-timezone@^0.2.0:
dependencies:
classnames "^2.2.1"
react-transition-group@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.2.0.tgz#793bf8cb15bfe91b3101b24bce1c1d2891659575"
dependencies:
chain-function "^1.0.0"
classnames "^2.2.5"
dom-helpers "^3.2.0"
loose-envify "^1.3.1"
prop-types "^15.5.8"
warning "^3.0.0"
react@^15.6.1:
version "15.6.1"
resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df"