mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
chore(ui): better modal animation, based on bootstrap code
This commit is contained in:
42
ui/src/Components/Animations/MountModal/index.js
Normal file
42
ui/src/Components/Animations/MountModal/index.js
Normal 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 };
|
||||
49
ui/src/Components/Animations/MountModal/index.scss
Normal file
49
ui/src/Components/Animations/MountModal/index.scss
Normal 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;
|
||||
}
|
||||
@@ -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>×</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>×</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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ const FakeModal = () => {
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={onHide}
|
||||
isVisible={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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\\"
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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">×</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">×</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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user