mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-09-04 12:14:37 +08:00
Add animation, multiple alerts, make them float
This commit is contained in:
parent
f6c7e854ad
commit
6951755a92
7 changed files with 167 additions and 48 deletions
|
@ -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> {message.text}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span> {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;
|
79
app/javascript/src/components/AlertsContainer/index.jsx
Normal file
79
app/javascript/src/components/AlertsContainer/index.jsx
Normal 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;
|
|
@ -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;
|
||||
|
|
27
app/javascript/src/styles/animations.scss
Normal file
27
app/javascript/src/styles/animations.scss
Normal 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;
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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",
|
||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Add table
Reference in a new issue