Use custom stack

This commit is contained in:
Zack Rauen 2023-04-11 18:30:36 -04:00
parent b5ad46a63f
commit 88d9687575
4 changed files with 177 additions and 5 deletions

View File

@ -1,6 +1,6 @@
import {Config} from "data";
import Logger from "common/logger";
import {WebpackModules, React, ReactDOM, Settings, Strings, DOMManager} from "modules";
import {WebpackModules, React, ReactDOM, Settings, Strings, DOMManager, Events} from "modules";
import FormattableString from "../structs/string";
import AddonErrorModal from "./modals/addonerrormodal";
import ErrorBoundary from "./errorboundary";
@ -14,6 +14,7 @@ import ConfirmationModal from "./modals/confirmation";
import Button from "./base/button";
import CustomMarkdown from "./base/markdown";
import ChangelogModal from "./modals/changelog";
import ModalStack, {generateKey} from "./modals/stack";
export default class Modals {
@ -172,7 +173,7 @@ export default class Modals {
if (!Array.isArray(content)) content = [content];
content = content.map(c => typeof(c) === "string" ? React.createElement(CustomMarkdown, null, c) : c);
const modalKey = ModalActions.openModal(props => {
const modalKey = this.openModal(props => {
return React.createElement(ErrorBoundary, {
onError: () => {
setTimeout(() => {
@ -204,7 +205,7 @@ export default class Modals {
pluginErrors: Array.isArray(pluginErrors) ? pluginErrors : [],
themeErrors: Array.isArray(themeErrors) ? themeErrors : []
};
this.ModalActions.openModal(props => {
this.openModal(props => {
return React.createElement(ErrorBoundary, null, React.createElement(AddonErrorModal, Object.assign(options, props)));
});
}
@ -212,7 +213,7 @@ export default class Modals {
static showChangelogModal(options = {}) {
options = Object.assign({image: "https://i.imgur.com/wuh5yMK.png", description: "", changes: [], title: "BetterDiscord", subtitle: `v${Config.version}`}, options);
const key = this.ModalActions.openModal(props => {
const key = this.openModal(props => {
return React.createElement(ErrorBoundary, null, React.createElement(ChangelogModal, Object.assign(options, props)));
});
return key;
@ -263,4 +264,23 @@ export default class Modals {
return React.createElement(ErrorBoundary, null, React.createElement(ConfirmationModal, Object.assign(options, props), child));
});
}
static makeStack() {
const div = DOMManager.parseHTML(`<div id="bd-modal-container">`);
DOMManager.bdBody.append(div);
ReactDOM.render(<ModalStack />, div);
this.hasInitialized = true;
}
static openModal(render, options = {}) {
if (!this.hasInitialized) this.makeStack();
options.modalKey = generateKey(options.modalKey);
Events.emit("open-modal", render, options);
return options.modalKey;
}
}
Modals.makeStack();

View File

@ -0,0 +1,33 @@
import {React, Utilities, WebpackModules} from "modules";
const Spring = WebpackModules.getByProps("useSpring", "animated");
export default function Backdrop({isVisible, className, onClick}) {
const transition = Spring.useTransition(isVisible, {
keys: e => e ? "backdrop" : "empty",
config: {duration: 300},
from: {
opacity: 0,
background: "var(--black-500)"
},
enter: {
opacity: 0.85,
background: "var(--black-500)"
},
leave: {
opacity: 0,
background: "var(--black-500)"
}
});
return transition((styles, visible) => {
if (!visible) return null;
return <Spring.animated.div
className={Utilities.className("bd-modal-backdrop", className)}
style={styles}
onClick={onClick}
/>;
});
}

View File

@ -18,7 +18,8 @@ export const Styles = Object.freeze({
export default function ModalRoot({className, transitionState, children, size = Sizes.DYNAMIC, style = Styles.CUSTOM}) {
const visible = transitionState == 0 || transitionState == 1;
const visible = transitionState == 0 || transitionState == 1; // 300 ms
// const visible = transitionState;
const springStyles = Spring.useSpring({
opacity: visible ? 1 : 0,
transform: visible ? "scale(1)" : "scale(0.7)",
@ -28,12 +29,57 @@ export default function ModalRoot({className, transitionState, children, size =
clamp: true
}
});
return <Spring.animated.div
className={Utilities.className("bd-modal-root", size, className, style)}
style={springStyles}
>
{children}
</Spring.animated.div>;
// const [visible, setVisible] = React.useState(true);
// const visible = transitionState < 2;
// const springTransition = Spring.useTransition(transitionState, {
// keys: e => e ? "backdrop" : "empty",
// from: {
// opacity: 0,
// transform: "scale(0.7)"
// },
// enter: {
// opacity: 1,
// transform: "scale(1)"
// },
// leave: {
// opacity: 0,
// transform: "scale(0.7)"
// },
// // config: (a, b, c, d) => {
// // console.log({a, b, c, d});
// // return {
// // duration: a ? 300 : 100,
// // easing: a ? Anims.Easing.inOut(Anims.Easing.back()) : Anims.Easing.quad,
// // clamp: true
// // };
// // }
// config: (a, b, c) => {
// console.log({a, b, c});
// return {
// duration: true ? 300 : 100,
// easing: true ? Anims.Easing.inOut(Anims.Easing.back()) : Anims.Easing.quad,
// clamp: true
// };
// }
// });
// return springTransition((styles, isVisible) => {
// if (!isVisible) console.log("not visible");
// return <Spring.animated.div
// className={Utilities.className("bd-modal-root", size, className, style)}
// style={styles}
// >
// {children}
// </Spring.animated.div>;
// });
}
ModalRoot.Sizes = Sizes;

View File

@ -0,0 +1,73 @@
import {React, Events, WebpackModules} from "modules";
import Backdrop from "./backdrop";
const {Fragment, useState, useCallback, useEffect} = React;
const Transitions = WebpackModules.getModule(m => m?.defaultProps?.transitionAppear);
// const Transitions = WebpackModules.getByProps("TransitionGroup").TransitionGroup;
class ModalLayer extends React.Component {
constructor(props) {
super(props);
this.state = {transitionState: null};
}
componentWillEnter(finish) {
this.setState({transitionState: 0});
setTimeout(() => {
this.setState({transitionState: 1});
finish();
}, 300);
}
componentWillLeave(finish) {
this.setState({transitionState: 2});
setTimeout(() => {
this.setState({transitionState: 3});
finish();
}, 300);
}
render() {
return React.createElement("div", {className: "bd-modal-layer"}, this.props.render({
transitionState: this.state.transitionState,
onClose: this.props.onClose
}));
}
}
// ENTERING 0
// ENTERED 1
// EXITING 2
// EXITED 3
// HIDDEN 4
let modalKey = 0;
export const generateKey = key => key ? `${key}-${modalKey++}` : modalKey++;
export default function ModalStack() {
const [modals, setModals] = useState([]);
const addModal = useCallback((render, props = {}) => {
setModals(m => [...m, {...props, render}]);
}, []);
const removeModal = useCallback((key) => {
setModals(mods => mods.filter(m => {
if (m.modalKey === key && m.onClose) m.onClose();
return m.modalKey !== key;
}));
}, []);
useEffect(() => {
Events.on("open-modal", addModal);
return () => {
Events.off("open-modal", addModal);
};
}, [addModal]);
return <Transitions component={Fragment}>
<Backdrop isVisible={!!modals.length} onClick={() => removeModal(modals[modals.length - 1].modalKey)} />
{modals.length && <ModalLayer key={modals[modals.length - 1].modalKey} {...modals[modals.length - 1]} onClose={() => removeModal(modals[modals.length - 1].modalKey)} />}
</Transitions>;
}