chore(ui): better modal animation, based on bootstrap code

This commit is contained in:
Łukasz Mierzwa
2018-10-04 19:38:19 +01:00
parent 8ec0bea3b0
commit 66588d10d3
8 changed files with 176 additions and 67 deletions

View File

@@ -0,0 +1,42 @@
import React from "react";
import PropTypes from "prop-types";
import { CSSTransition } from "react-transition-group";
import "./index.scss";
const MountModal = ({ children, duration, ...props }) => (
<CSSTransition
in={true}
classNames="components-animation-modal"
timeout={200}
appear={true}
enter={true}
exit={true}
{...props}
>
{children}
</CSSTransition>
);
MountModal.propTypes = {
children: PropTypes.node.isRequired
};
const MountModalBackdrop = ({ children, duration, ...props }) => (
<CSSTransition
in={true}
classNames="components-animation-backdrop"
timeout={200}
appear={true}
enter={true}
exit={true}
{...props}
>
{children}
</CSSTransition>
);
MountModalBackdrop.propTypes = {
children: PropTypes.node.isRequired
};
export { MountModal, MountModalBackdrop };

View File

@@ -0,0 +1,49 @@
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
$duration: 0.2s;
.components-animation-modal-appear,
.components-animation-modal-enter {
opacity: 0.01;
transform: translate(0, -25%);
}
.components-animation-modal-appear-active,
.components-animation-modal-enter-active {
opacity: 1;
transition: transform $duration ease-out, opacity $duration ease-in;
transform: translate(0, 0);
}
.components-animation-modal-exit {
opacity: 1;
transform: translate(0, 0);
}
.components-animation-modal-exit-active {
opacity: 0.01;
transform: translate(0, -25%);
transition: transform $duration ease-in, opacity $duration ease-out;
}
.components-animation-backdrop-appear,
.components-animation-backdrop-enter {
opacity: 0.01;
}
.components-animation-backdrop-appear-active,
.components-animation-backdrop-enter-active {
opacity: $modal-backdrop-opacity;
transition: opacity $duration ease-in;
}
.components-animation-backdrop-enter-done {
opacity: $modal-backdrop-opacity;
transition: opacity $duration ease-in;
}
.components-animation-backdrop-exit {
opacity: $modal-backdrop-opacity;
}
.components-animation-backdrop-exit-active {
opacity: 0.01;
transition: opacity $duration ease-out;
}

View File

@@ -10,6 +10,7 @@ import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
import { AlertStore } from "Stores/AlertStore";
import { Settings } from "Stores/Settings";
import { Configuration } from "./Configuration";
import { MountModalBackdrop } from "Components/Animations/MountModal";
import { Help } from "./Help";
const Tab = ({ title, active, onClick }) => (
@@ -38,7 +39,8 @@ const MainModalContent = observer(
static propTypes = {
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
settingsStore: PropTypes.instanceOf(Settings).isRequired,
onHide: PropTypes.func.isRequired
onHide: PropTypes.func.isRequired,
isVisible: PropTypes.bool.isRequired
};
tab = observable(
@@ -60,43 +62,48 @@ const MainModalContent = observer(
}
render() {
const { alertStore, settingsStore, onHide } = this.props;
const { alertStore, settingsStore, onHide, isVisible } = this.props;
return ReactDOM.createPortal(
<div className="modal d-block bg-primary-transparent-80" role="dialog">
<div className="modal-dialog modal-lg" role="document">
<div className="modal-content">
<div className="modal-header py-2">
<nav className="nav nav-pills nav-justified w-100">
<Tab
title="Configuration"
active={this.tab.current === TabNames.Configuration}
onClick={() => this.tab.setTab(TabNames.Configuration)}
/>
<Tab
title="Help"
active={this.tab.current === TabNames.Help}
onClick={() => this.tab.setTab(TabNames.Help)}
/>
<button type="button" className="close" onClick={onHide}>
<span>&times;</span>
</button>
</nav>
</div>
<div className="modal-body">
{this.tab.current === TabNames.Help ? <Help /> : null}
{this.tab.current === TabNames.Configuration ? (
<Configuration settingsStore={settingsStore} />
) : null}
</div>
<div className="modal-footer">
<span className="text-muted">
Version: {alertStore.info.version}
</span>
<React.Fragment>
<div className="modal d-block" role="dialog">
<div className="modal-dialog modal-lg" role="document">
<div className="modal-content">
<div className="modal-header py-2">
<nav className="nav nav-pills nav-justified w-100">
<Tab
title="Configuration"
active={this.tab.current === TabNames.Configuration}
onClick={() => this.tab.setTab(TabNames.Configuration)}
/>
<Tab
title="Help"
active={this.tab.current === TabNames.Help}
onClick={() => this.tab.setTab(TabNames.Help)}
/>
<button type="button" className="close" onClick={onHide}>
<span>&times;</span>
</button>
</nav>
</div>
<div className="modal-body">
{this.tab.current === TabNames.Help ? <Help /> : null}
{this.tab.current === TabNames.Configuration ? (
<Configuration settingsStore={settingsStore} />
) : null}
</div>
<div className="modal-footer">
<span className="text-muted">
Version: {alertStore.info.version}
</span>
</div>
</div>
</div>
</div>
</div>,
<MountModalBackdrop in={isVisible} unmountOnExit>
<div className="modal-backdrop d-block" />
</MountModalBackdrop>
</React.Fragment>,
document.body
);
}

View File

@@ -28,6 +28,7 @@ const FakeModal = () => {
alertStore={alertStore}
settingsStore={settingsStore}
onHide={onHide}
isVisible={true}
/>
);
};

View File

@@ -2,7 +2,7 @@
exports[`<MainModalContent /> matches snapshot 1`] = `
"
<div class=\\"modal d-block bg-primary-transparent-80\\"
<div class=\\"modal d-block\\"
role=\\"dialog\\"
>
<div class=\\"modal-dialog modal-lg\\"

View File

@@ -9,7 +9,7 @@ import { faCog } from "@fortawesome/free-solid-svg-icons/faCog";
import { AlertStore } from "Stores/AlertStore";
import { Settings } from "Stores/Settings";
import { MountFade } from "Components/Animations/MountFade";
import { MountModal } from "Components/Animations/MountModal";
import { MainModalContent } from "./MainModalContent";
const MainModal = observer(
@@ -53,13 +53,14 @@ const MainModal = observer(
<FontAwesomeIcon icon={faCog} />
</span>
</li>
<MountFade in={this.toggle.show} unmountOnExit>
<MountModal in={this.toggle.show} unmountOnExit>
<MainModalContent
alertStore={alertStore}
settingsStore={settingsStore}
onHide={this.toggle.hide}
isVisible={this.toggle.show}
/>
</MountFade>
</MountModal>
</React.Fragment>
);
}

View File

@@ -9,6 +9,7 @@ import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore } from "Stores/SilenceFormStore";
import { Settings } from "Stores/Settings";
import { MountModalBackdrop } from "Components/Animations/MountModal";
import { SilenceForm } from "./SilenceForm";
import { SilenceSubmitController } from "./SilenceSubmitController";
@@ -38,36 +39,44 @@ const SilenceModalContent = observer(
} = this.props;
return ReactDOM.createPortal(
<div className="modal d-block bg-primary-transparent-80" role="dialog">
<div className="modal-dialog modal-lg" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">
{silenceFormStore.data.silenceID === null
? "Add new silence"
: `Editing silence ${silenceFormStore.data.silenceID}`}
</h5>
<button type="button" className="close" onClick={onHide}>
<span className="align-middle">&times;</span>
</button>
</div>
<div className="modal-body">
{silenceFormStore.data.inProgress ? (
<SilenceSubmitController
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
) : (
<SilenceForm
alertStore={alertStore}
silenceFormStore={silenceFormStore}
settingsStore={settingsStore}
/>
)}
<React.Fragment>
<div className="modal d-block" role="dialog">
<div className="modal-dialog modal-lg" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">
{silenceFormStore.data.silenceID === null
? "Add new silence"
: `Editing silence ${silenceFormStore.data.silenceID}`}
</h5>
<button type="button" className="close" onClick={onHide}>
<span className="align-middle">&times;</span>
</button>
</div>
<div className="modal-body">
{silenceFormStore.data.inProgress ? (
<SilenceSubmitController
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
) : (
<SilenceForm
alertStore={alertStore}
silenceFormStore={silenceFormStore}
settingsStore={settingsStore}
/>
)}
</div>
</div>
</div>
</div>
</div>,
<MountModalBackdrop
in={silenceFormStore.toggle.visible}
unmountOnExit
>
<div className="modal-backdrop d-block" />
</MountModalBackdrop>
</React.Fragment>,
document.body
);
}

View File

@@ -9,7 +9,7 @@ import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash";
import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore } from "Stores/SilenceFormStore";
import { Settings } from "Stores/Settings";
import { MountFade } from "Components/Animations/MountFade";
import { MountModal } from "Components/Animations/MountModal";
import { SilenceModalContent } from "./SilenceModalContent";
import "./index.css";
@@ -60,14 +60,14 @@ const SilenceModal = observer(
<FontAwesomeIcon icon={faBellSlash} />
</span>
</li>
<MountFade in={silenceFormStore.toggle.visible} unmountOnExit>
<MountModal in={silenceFormStore.toggle.visible} unmountOnExit>
<SilenceModalContent
alertStore={alertStore}
silenceFormStore={silenceFormStore}
settingsStore={settingsStore}
onHide={this.toggleModal}
/>
</MountFade>
</MountModal>
</React.Fragment>
);
}