+
+export default function Checkbox({checked: initialState, text, onChange: notifyParent}) {
+ const [checked, setChecked] = useState(initialState);
+ const onClick = useCallback(() => {
+ notifyParent?.(!checked);
+ setChecked(!checked);
+ }, [notifyParent, checked]);
+
+ return
;
- }
-
- onClick() {
- this.props.onChange(!this.state.checked);
- this.setState({checked: !this.state.checked});
- }
}
\ No newline at end of file
diff --git a/renderer/src/ui/customcss/csseditor.jsx b/renderer/src/ui/customcss/csseditor.jsx
index c9ddc2cf..3c7bd776 100644
--- a/renderer/src/ui/customcss/csseditor.jsx
+++ b/renderer/src/ui/customcss/csseditor.jsx
@@ -1,91 +1,65 @@
import {React, Settings, Events, Strings} from "modules";
import Editor from "./editor";
-// import Checkbox from "./checkbox";
import Refresh from "../icons/reload";
import Save from "../icons/save";
import Edit from "../icons/edit";
import Detach from "../icons/detach";
-export default class CssEditor extends React.Component {
+const {useState, useCallback, useEffect, forwardRef, useImperativeHandle, useRef} = React;
- constructor(props) {
- super(props);
- this.hasUnsavedChanges = false;
+export default forwardRef(function CssEditor({css, openNative, update, save, onChange: notifyParent, readOnly = false, id = "bd-customcss-editor", openDetached = false}, ref) {
+ const editorRef = useRef(null);
+ const [hasUnsavedChanges, setUnsaved] = useState(false);
- this.onChange = this.onChange.bind(this);
- this.toggleLiveUpdate = this.toggleLiveUpdate.bind(this);
- this.updateCss = this.updateCss.bind(this);
- this.saveCss = this.saveCss.bind(this);
- this.openDetached = this.props.openDetached ? this.openDetached.bind(this) : null;
- this.openNative = this.openNative.bind(this);
- this.updateEditor = this.updateEditor.bind(this);
+ const updateEditor = useCallback((newCSS) => {
+ editorRef.current.value = newCSS;
+ }, [editorRef]);
- this.controls = [
- {label: React.createElement(Refresh, {size: "18px"}), tooltip: Strings.CustomCSS.update, onClick: this.updateCss},
- {label: React.createElement(Save, {size: "18px"}), tooltip: Strings.CustomCSS.save, onClick: this.saveCss},
- {label: React.createElement(Edit, {size: "18px"}), tooltip: Strings.CustomCSS.openNative, onClick: this.openNative},
- {label: Strings.Collections.settings.customcss.liveUpdate.name, type: "checkbox", onChange: this.toggleLiveUpdate, checked: Settings.get("settings", "customcss", "liveUpdate"), side: "right"}
- ];
- if (this.openDetached) this.controls.push({label: React.createElement(Detach, {size: "18px"}), tooltip: Strings.CustomCSS.openDetached, onClick: this.openDetached, side: "right"});
- }
+ useImperativeHandle(ref, () => {
+ return {
+ resize() {editorRef.current.resize();},
+ showSettings() {editorRef.current.showSettings();},
+ get value() {return editorRef.current.getValue();},
+ set value(newValue) {editorRef.current.setValue(newValue);},
+ get hasUnsavedChanges() {return hasUnsavedChanges;}
+ };
+ }, [hasUnsavedChanges]);
- componentDidMount() {
- Events.on("customcss-updated", this.updateEditor);
- }
+ useEffect(() => {
+ Events.on("customcss-updated", updateEditor);
+ return () => Events.off("customcss-updated", updateEditor);
+ }, [updateEditor]);
- componentWillUnmount() {
- Events.off("customcss-updated", this.updateEditor);
- }
+ const toggleLiveUpdate = useCallback((checked) => Settings.set("settings", "customcss", "liveUpdate", checked), []);
+ const updateCss = useCallback((event, newCSS) => update?.(newCSS), [update]);
+ const popoutNative = useCallback(() => openNative?.(), [openNative]);
+ const popout = useCallback((event, currentCSS) => openDetached?.(currentCSS), [openDetached]);
- updateEditor(newCSS) {
- if (!this.editor) return;
- this.editor.value = newCSS;
- }
+ const onChange = useCallback((newCSS) => {
+ notifyParent?.(newCSS);
+ setUnsaved(true);
+ }, [notifyParent]);
- get value() {return this.editor.session.getValue();}
- set value(newValue) {
- this.editor.setValue(newValue);
- }
+ const saveCss = useCallback((event, newCSS) => {
+ save?.(newCSS);
+ setUnsaved(false);
+ }, [save]);
+
- showSettings() {return this.editor.keyBinding.$defaultHandler.commands.showSettingsMenu.exec(this.editor);}
- resize() {return this.editor.resize();}
-
- setEditorRef(editor) {
- this.editor = editor;
- if (this.props.editorRef && typeof(this.props.editorRef.current) !== "undefined") this.props.editorRef.current = editor;
- else if (this.props.editorRef) this.props.editorRef = editor;
- }
-
- onChange() {
- this.hasUnsavedChanges = true;
- if (this.props.onChange) this.props.onChange(...arguments);
- }
-
- render() {
- return
;
- }
-
- toggleLiveUpdate(checked) {
- Settings.set("settings", "customcss", "liveUpdate", checked);
- }
-
- updateCss(event, newCss) {
- if (this.props.update) this.props.update(newCss);
- }
-
- saveCss(event, newCss) {
- this.hasUnsavedChanges = false;
- if (this.props.save) this.props.save(newCss);
- }
-
- openDetached(event, currentCSS) {
- if (!this.props.openDetached) return;
- this.props.openDetached(currentCSS);
- }
-
- openNative() {
- if (this.props.openNative) this.props.openNative();
- }
-}
\ No newline at end of file
+ return
, tooltip: Strings.CustomCSS.update, onClick: updateCss},
+ {label:
, tooltip: Strings.CustomCSS.save, onClick: saveCss},
+ {label:
, tooltip: Strings.CustomCSS.openNative, onClick: popoutNative},
+ {label: Strings.Collections.settings.customcss.liveUpdate.name, type: "checkbox", onChange: toggleLiveUpdate, checked: Settings.get("settings", "customcss", "liveUpdate"), side: "right"},
+ openDetached && {label:
, tooltip: Strings.CustomCSS.openDetached, onClick: popout, side: "right"}
+ ].filter(c => c)}
+ value={css}
+ />;
+});
\ No newline at end of file
diff --git a/renderer/src/ui/customcss/editor.jsx b/renderer/src/ui/customcss/editor.jsx
index d73f8a86..bc79399a 100644
--- a/renderer/src/ui/customcss/editor.jsx
+++ b/renderer/src/ui/customcss/editor.jsx
@@ -2,41 +2,79 @@ import {React, DiscordModules, Settings} from "modules";
import Checkbox from "./checkbox";
+const {useState, useCallback, useEffect, forwardRef, useMemo, useImperativeHandle} = React;
const ThemeStore = DiscordModules.ThemeStore;
+
const languages = ["abap", "abc", "actionscript", "ada", "apache_conf", "asciidoc", "assembly_x86", "autohotkey", "batchfile", "bro", "c_cpp", "c9search", "cirru", "clojure", "cobol", "coffee", "coldfusion", "csharp", "csound_document", "csound_orchestra", "csound_score", "css", "curly", "d", "dart", "diff", "dockerfile", "dot", "drools", "dummy", "dummysyntax", "eiffel", "ejs", "elixir", "elm", "erlang", "forth", "fortran", "ftl", "gcode", "gherkin", "gitignore", "glsl", "gobstones", "golang", "graphqlschema", "groovy", "haml", "handlebars", "haskell", "haskell_cabal", "haxe", "hjson", "html", "html_elixir", "html_ruby", "ini", "io", "jack", "jade", "java", "javascript", "json", "jsoniq", "jsp", "jssm", "jsx", "julia", "kotlin", "latex", "less", "liquid", "lisp", "livescript", "logiql", "lsl", "lua", "luapage", "lucene", "makefile", "markdown", "mask", "matlab", "maze", "mel", "mushcode", "mysql", "nix", "nsis", "objectivec", "ocaml", "pascal", "perl", "pgsql", "php", "pig", "powershell", "praat", "prolog", "properties", "protobuf", "python", "r", "razor", "rdoc", "red", "rhtml", "rst", "ruby", "rust", "sass", "scad", "scala", "scheme", "scss", "sh", "sjs", "smarty", "snippets", "soy_template", "space", "sql", "sqlserver", "stylus", "svg", "swift", "tcl", "tex", "text", "textile", "toml", "tsx", "twig", "typescript", "vala", "vbscript", "velocity", "verilog", "vhdl", "wollok", "xml", "xquery", "yaml", "django"];
-export default class CodeEditor extends React.Component {
- static get defaultId() {return "bd-editor";}
+function makeButton(button, value) {
+ return
+ {props => {
+ return ;
+ }}
+ ;
+}
- constructor(props) {
- super(props);
+function makeCheckbox(checkbox) {
+ return
;
+}
- this.props.theme = ThemeStore?.theme === "light" ? "vs" : "vs-dark";
+function buildControl(value, control) {
+ if (control.type == "checkbox") return makeCheckbox(control);
+ return makeButton(control, value);
+}
- this.props.language = this.props.language.toLowerCase().replace(/ /g, "_");
- if (!languages.includes(this.props.language)) this.props.language = CodeEditor.defaultProps.language;
+export default forwardRef(function CodeEditor({value, language: requestedLang = "css", id = "bd-editor", controls = [], onChange: notifyParent}, ref) {
+ const language = useMemo(() => {
+ const requested = requestedLang.toLowerCase().replace(/ /g, "_");
+ if (!languages.includes(requested)) return "css";
+ return requested;
+ }, [requestedLang]);
- this.bindings = [];
- this.resize = this.resize.bind(this);
- this.onChange = this.onChange.bind(this);
- this.onThemeChange = this.onThemeChange.bind(this);
- }
+ const [theme, setTheme] = useState(() => ThemeStore?.theme === "light" ? "vs" : "vs-dark");
+ const [editor, setEditor] = useState(null);
+ const [, setBindings] = useState([]);
- static get defaultProps() {
+ const onThemeChange = useCallback(() => {
+ const newTheme = ThemeStore?.theme === "light" ? "vs" : "vs-dark";
+ if (newTheme === theme) return;
+ if (window.monaco?.editor) window.monaco.editor.setTheme(newTheme);
+ setTheme(newTheme);
+ }, [theme]);
+
+ const onChange = useCallback(() => {
+ notifyParent?.(editor?.getValue());
+ }, [editor, notifyParent]);
+ const resize = useCallback(() => editor.layout(), [editor]);
+ const showSettings = useCallback(() => editor.keyBinding.$defaultHandler.commands.showSettingsMenu.exec(editor), [editor]);
+
+ useImperativeHandle(ref, () => {
return {
- controls: [],
- language: "css",
- id: this.defaultId
+ resize,
+ showSettings,
+ get value() {return editor.getValue();},
+ set value(newValue) {editor.setValue(newValue);}
};
- }
+ }, [editor, resize, showSettings]);
- componentDidMount() {
+ useEffect(() => {
+ setBindings(bins => [...bins, editor?.onDidChangeModelContent(onChange)]);
+ return () => {
+ setBindings(bins => {
+ for (const binding of bins) binding?.dispose();
+ return [];
+ });
+ };
+ }, [editor, onChange]);
+
+ useEffect(() => {
+ let toDispose = null;
if (window.monaco?.editor) {
- this.editor = window.monaco.editor.create(document.getElementById(this.props.id), {
- value: this.props.value,
- language: this.props.language,
- theme: ThemeStore?.theme == "light" ? "vs" : "vs-dark",
+ const monacoEditor = window.monaco.editor.create(document.getElementById(id), {
+ value: value,
+ language: language,
+ theme: theme,
fontSize: Settings.get("settings", "editor", "fontSize"),
lineNumbers: Settings.get("settings", "editor", "lineNumbers"),
minimap: {enabled: Settings.get("settings", "editor", "minimap")},
@@ -49,89 +87,61 @@ export default class CodeEditor extends React.Component {
renderWhitespace: Settings.get("settings", "editor", "renderWhitespace")
});
- this.bindings.push(this.editor.onDidChangeModelContent(this.onChange));
+ toDispose = monacoEditor;
+ setEditor(monacoEditor);
}
else {
const textarea = document.createElement("textarea");
textarea.className = "bd-fallback-editor";
- textarea.value = this.props.value;
- textarea.onchange = (e) => this.onChange(e.target.value);
- textarea.oninput = (e) => this.onChange(e.target.value);
+ textarea.value = value;
- this.editor = {
+ setEditor({
dispose: () => textarea.remove(),
getValue: () => textarea.value,
- setValue: (value) => textarea.value = value,
+ setValue: (val) => textarea.value = val,
layout: () => {},
- };
+ onDidChangeModelContent: (cb) => {
+ textarea.onchange = cb;
+ textarea.oninput = cb;
+ }
+ });
- document.getElementById(this.props.id).appendChild(textarea);
+ document.getElementById(id).appendChild(textarea);
}
- ThemeStore?.addChangeListener?.(this.onThemeChange);
- window.addEventListener("resize", this.resize);
- }
+ return () => {
+ toDispose?.dispose?.();
+ };
+ }, [id, language, theme, value]);
- componentWillUnmount() {
- window.removeEventListener("resize", this.resize);
- ThemeStore?.removeChangeListener?.(this.onThemeChange);
- for (const binding of this.bindings) binding.dispose();
- this.editor.dispose();
- }
+ useEffect(() => {
+ ThemeStore?.addChangeListener?.(onThemeChange);
+ window.addEventListener("resize", resize);
- onThemeChange() {
- const newTheme = ThemeStore?.theme === "light" ? "vs" : "vs-dark";
- if (newTheme === this.props.theme) return;
- this.props.theme = newTheme;
- if (window.monaco?.editor) window.monaco.editor.setTheme(this.props.theme);
- }
+ return () => {
+ window.removeEventListener("resize", resize);
+ ThemeStore?.removeChangeListener?.(onThemeChange);
+ };
+ }, [onThemeChange, resize]);
- get value() {return this.editor.getValue();}
- set value(newValue) {this.editor.setValue(newValue);}
- onChange() {
- if (this.props.onChange) this.props.onChange(this.value);
- }
+ if (editor && editor.layout) editor.layout();
- showSettings() {return this.editor.keyBinding.$defaultHandler.commands.showSettingsMenu.exec(this.editor);}
- resize() {this.editor.layout();}
+ const controlsLeft = controls.filter(c => c.side != "right").map(buildControl.bind(null, () => editor?.getValue()));
+ const controlsRight = controls.filter(c => c.side == "right").map(buildControl.bind(null, () => editor?.getValue()));
- buildControl(control) {
- if (control.type == "checkbox") return this.makeCheckbox(control);
- return this.makeButton(control);
- }
-
- makeCheckbox(checkbox) {
- return
;
- }
-
- makeButton(button) {
- return
- {props => {
- return ;
- }}
- ;
- }
-
- render() {
- if (this.editor && this.editor.layout) this.editor.layout();
-
- const controlsLeft = this.props.controls.filter(c => c.side != "right").map(this.buildControl.bind(this));
- const controlsRight = this.props.controls.filter(c => c.side == "right").map(this.buildControl.bind(this));
-
- return
-
-
- {controlsLeft}
-
-
- {controlsRight}
-
+ return
+
+
+ {controlsLeft}
-
;
- }
-}
+
+
+
;
+});
\ No newline at end of file
diff --git a/renderer/src/ui/floating/container.jsx b/renderer/src/ui/floating/container.jsx
index 17536df3..50008ad3 100644
--- a/renderer/src/ui/floating/container.jsx
+++ b/renderer/src/ui/floating/container.jsx
@@ -1,54 +1,36 @@
-import {React} from "modules";
+import {React, Events} from "modules";
import FloatingWindow from "./window";
-class FloatingWindowContainer extends React.Component {
+const {useState, useCallback, useEffect} = React;
- constructor(props) {
- super(props);
- this.state = {windows: []};
- }
- get minY() {
- const appContainer = document.querySelector(`#app-mount > div[class*="app-"]`);
- if (appContainer) return appContainer.offsetTop;
- return 0;
- }
-
- render() {
- return this.state.windows.map(window =>
-
- {window.children}
-
- );
- }
-
- open(window) {
- this.setState(state => {
- state.windows.push(window);
- return {windows: state.windows};
- });
- }
-
- close(id) {
- this.setState(state => {
- return {
- windows: state.windows.filter(w => {
- if (w.id == id && w.onClose) w.onClose();
- return w.id != id;
- })
- };
- });
- }
-
- static get id() {return "floating-windows";}
- static get root() {
- if (this._root) return this._root;
- const container = document.createElement("div");
- container.id = this.id;
- document.body.append(container);
- return this._root = container;
- }
+function minY() {
+ const appContainer = document.querySelector(`#app-mount > div[class*="app-"]`);
+ if (appContainer) return appContainer.offsetTop;
+ return 0;
}
-export default FloatingWindowContainer;
\ No newline at end of file
+export default function FloatingWindowContainer() {
+ const [windows, setWindows] = useState([]);
+ const open = useCallback(window => {
+ setWindows(wins => [...wins, window]);
+ }, []);
+ const close = useCallback(id => {
+ setWindows(windows.filter(w => {
+ if (w.id === id && w.onClose) w.onClose();
+ return w.id !== id;
+ }));
+ }, [windows]);
+
+ useEffect(() => {
+ Events.on("open-window", open);
+ return () => Events.off("open-window", open);
+ }, [open]);
+
+ return windows.map(window =>
+
close(window.id)} minY={minY()} key={window.id}>
+ {window.children}
+
+ );
+}
\ No newline at end of file
diff --git a/renderer/src/ui/floating/window.jsx b/renderer/src/ui/floating/window.jsx
index 33c2a18e..220b9844 100644
--- a/renderer/src/ui/floating/window.jsx
+++ b/renderer/src/ui/floating/window.jsx
@@ -5,174 +5,153 @@ import CloseButton from "../icons/close";
import MaximizeIcon from "../icons/fullscreen";
import Modals from "../modals";
-// const Draggable = WebpackModules.getByDisplayName("Draggable");
-// {
-// "dragAnywhere": true,
-// "className": "pictureInPictureWindow-1B5qSe",
-// "maxX": 1969,
-// "maxY": this.maxY,
-// "onDragStart": "ƒ () {}",
-// "onDrag": "ƒ () {}",
-// "onDragEnd": "ƒ () {}",
-// "children": "
",
-// "initialX": 0,
-// "initialY": 0
-// }
+const {useState, useCallback, useEffect, useRef} = React;
-export default class FloatingWindow extends React.Component {
- constructor(props) {
- super(props);
+function confirmClose(confirmationText) {
+ return new Promise(resolve => {
+ Modals.showConfirmationModal(Strings.Modals.confirmAction, confirmationText, {
+ danger: true,
+ confirmText: Strings.Modals.close,
+ onConfirm: () => {resolve(true);},
+ onCancel: () => {resolve(false);}
+ });
+ });
+}
- this.state = {modalOpen: false};
+export default function FloatingWindow({id, title, resizable, children, className, center, top: initialTop, left: initialLeft, width: initialWidth, height: initialHeight, minX = 0, minY = 0, maxX = Screen.width, maxY = Screen.height, onResize, close: doClose, confirmClose: doConfirmClose, confirmationText}) {
+ const [modalOpen, setOpen] = useState(false);
+ const [isDragging, setDragging] = useState(false);
+ const [position, setPosition] = useState({x: center ? (Screen.width / 2) - (initialWidth / 2) : initialLeft, y: center ? (Screen.height / 2) - (initialHeight / 2) : initialTop});
+ const [offset, setOffset] = useState({x: 0, y: 0});
+ const [size, setSize] = useState({width: 0, height: 0});
- this.offX = 0;
- this.offY = 0;
+ const titlebar = useRef(null);
+ const window = useRef(null);
- this.maxX = this.props.maxX || Screen.width;
- this.maxY = this.props.maxY || Screen.height;
- this.minX = this.props.minX || 0;
- this.minY = this.props.minY || 0;
- this.titlebar = React.createRef();
- this.window = React.createRef();
+ const onResizeStart = useCallback(() => {
+ setSize({width: window.current.offsetWidth, height: window.current.offsetHeight});
+ }, [window]);
- this.close = this.close.bind(this);
- this.maximize = this.maximize.bind(this);
- this.onDrag = this.onDrag.bind(this);
- this.onDragStart = this.onDragStart.bind(this);
- this.onDragStop = this.onDragStop.bind(this);
- this.onResizeStart = this.onResizeStart.bind(this);
- }
- componentDidMount() {
- this.window.current.addEventListener("mousedown", this.onResizeStart, false);
- this.titlebar.current.addEventListener("mousedown", this.onDragStart, false);
- document.addEventListener("mouseup", this.onDragStop, false);
- }
+ const onDrag = useCallback((e) => {
+ if (!isDragging) return;
+ let newTop = (e.clientY - offset.y);
+ if (newTop <= minY) newTop = minY;
+ if (newTop + size.height >= maxY) newTop = maxY - size.height;
- onResizeStart() {
- this.currentWidth = this.window.current.offsetWidth;
- this.currentHeight = this.window.current.offsetHeight;
- }
+ let newLeft = (e.clientX - offset.x);
+ if (newLeft <= minX) newLeft = minX;
+ if (newLeft + size.width >= maxX) newLeft = maxX - size.width;
- onDragStop() {
- document.removeEventListener("mousemove", this.onDrag, true);
- const width = this.window.current.offsetWidth;
- const height = this.window.current.offsetHeight;
- if (width != this.currentWidth || height != this.currentHeight) {
- if (this.props.onResize) this.props.onResize();
- const left = parseInt(this.window.current.style.left);
- const top = parseInt(this.window.current.style.top);
- if (left + width >= this.maxX) this.window.current.style.width = (this.maxX - left) + "px";
- if (top + height >= this.maxY) this.window.current.style.height = (this.maxY - top) + "px";
+ setPosition({x: newLeft, y: newTop});
+ }, [offset, size, isDragging, minX, minY, maxX, maxY]);
+
+
+ const onDragStart = useCallback((e) => {
+ const div = window.current;
+ setOffset({x: e.clientX - parseInt(div.offsetLeft), y: e.clientY - parseInt(div.offsetTop)});
+ setDragging(true);
+ }, [window]);
+
+
+ const onDragStop = useCallback(() => {
+ setDragging(false);
+ const width = window.current.offsetWidth;
+ const height = window.current.offsetHeight;
+ if (width != size.width || height != size.height) {
+ if (onResize) onResize();
+ const left = parseInt(window.current.style.left);
+ const top = parseInt(window.current.style.top);
+ if (left + width >= maxX) window.current.style.width = (maxX - left) + "px";
+ if (top + height >= maxY) window.current.style.height = (maxY - top) + "px";
}
- this.currentWidth = width;
- this.currentHeight = height;
- }
- onDragStart(e) {
- const div = this.window.current;
- this.offY = e.clientY - parseInt(div.offsetTop);
- this.offX = e.clientX - parseInt(div.offsetLeft);
- document.addEventListener("mousemove", this.onDrag, true);
- }
+ setSize({width, height});
+ }, [window, size, maxX, maxY, onResize]);
- onDrag(e) {
- const div = this.window.current;
- let newTop = (e.clientY - this.offY);
- if (newTop <= this.minY) newTop = this.minY;
- if (newTop + this.currentHeight >= this.maxY) newTop = this.maxY - this.currentHeight;
- let newLeft = (e.clientX - this.offX);
- if (newLeft <= this.minX) newLeft = this.minX;
- if (newLeft + this.currentWidth >= this.maxX) newLeft = this.maxX - this.currentWidth;
+ useEffect(() => {
+ const winRef = window.current;
+ const titleRef = titlebar.current;
+ winRef.addEventListener("mousedown", onResizeStart, false);
+ titleRef.addEventListener("mousedown", onDragStart, false);
+ document.addEventListener("mouseup", onDragStop, false);
+ document.addEventListener("mousemove", onDrag, true);
- div.style.top = newTop + "px";
- div.style.left = newLeft + "px";
- }
+ return () => {
+ document.removeEventListener("mouseup", onDragStop, false);
+ document.removeEventListener("mousemove", onDrag, true);
+ winRef.removeEventListener("mousedown", onResizeStart, false);
+ titleRef.removeEventListener("mousedown", onDragStart, false);
+ };
+ }, [titlebar, window, onDragStart, onDragStop, onDrag, onResizeStart]);
- componentWillUnmount() {
- this.titlebar.current.removeEventListener("mousedown", this.onDragStart, false);
- document.removeEventListener("mouseup", this.onDragStop, false);
- }
- render() {
- const top = this.props.center ? (Screen.height / 2) - (this.props.height / 2) : this.props.top;
- const left = this.props.center ? (Screen.width / 2) - (this.props.width / 2) : this.props.left;
- // console.log(top, left);
- const className = `floating-window${` ${this.props.className}` || ""}${this.props.resizable ? " resizable" : ""}${this.state.modalOpen ? " modal-open" : ""}`;
- const styles = {height: this.props.height, width: this.props.width, left: left || 0, top: top || 0};
- return
-
-
- {this.props.children}
-
-
;
- }
+ const maximize = useCallback(() => {
+ window.current.style.width = "100%";
+ window.current.style.height = "100%";
+ if (onResize) onResize();
- maximize() {
- this.window.current.style.width = "100%";
- this.window.current.style.height = "100%";
- if (this.props.onResize) this.props.onResize();
-
- const width = this.window.current.offsetWidth;
- const height = this.window.current.offsetHeight;
- const left = parseInt(this.window.current.style.left);
- const top = parseInt(this.window.current.style.top);
+ const width = window.current.offsetWidth;
+ const height = window.current.offsetHeight;
+ const left = parseInt(window.current.style.left);
+ const top = parseInt(window.current.style.top);
const right = left + width;
const bottom = top + height;
// Prevent expanding off the bottom and right and readjust position
- if (bottom > this.maxY) this.window.current.style.top = (this.maxY - height) + "px";
- if (right > this.maxX) this.window.current.style.left = (this.maxX - width) + "px";
+ if (bottom > maxY) window.current.style.top = (maxY - height) + "px";
+ if (right > maxX) window.current.style.left = (maxX - width) + "px";
- const newLeft = parseInt(this.window.current.style.left);
- const newTop = parseInt(this.window.current.style.top);
+ const newLeft = parseInt(window.current.style.left);
+ const newTop = parseInt(window.current.style.top);
- // For small screens it's possible this pushes us off the other direction... we need to readjust size
- if (newTop < this.minY) {
- const difference = this.minY - newTop;
- this.window.current.style.top = this.minY + "px";
- this.window.current.style.height = (height - difference) + "px";
+ // For small screens it's possible pushes us off the other direction... we need to readjust size
+ if (newTop < minY) {
+ const difference = minY - newTop;
+ window.current.style.top = minY + "px";
+ window.current.style.height = (height - difference) + "px";
}
- if (newLeft < this.minX) {
- const difference = this.minX - newLeft;
- this.window.current.style.left = this.minX + "px";
- this.window.current.style.height = (width - difference) + "px";
+ if (newLeft < minX) {
+ const difference = minX - newLeft;
+ window.current.style.left = minX + "px";
+ window.current.style.height = (width - difference) + "px";
}
- }
+ }, [window, minX, minY, maxX, maxY, onResize]);
- async close() {
+
+ const close = useCallback(async () => {
let shouldClose = true;
- const confirmClose = typeof(this.props.confirmClose) == "function" ? this.props.confirmClose() : this.props.confirmClose;
- if (confirmClose) {
- this.setState({modalOpen: true});
- shouldClose = await this.confirmClose();
- this.setState({modalOpen: false});
+ const didConfirmClose = typeof(doConfirmClose) == "function" ? doConfirmClose() : doConfirmClose;
+ if (didConfirmClose) {
+ setOpen(true);
+ shouldClose = await confirmClose(confirmationText);
+ setOpen(false);
}
- if (this.props.close && shouldClose) this.props.close();
- }
+ if (doClose && shouldClose) doClose();
+ }, [confirmationText, doClose, doConfirmClose]);
- confirmClose() {
- return new Promise(resolve => {
- Modals.showConfirmationModal(Strings.Modals.confirmAction, this.props.confirmationText, {
- danger: true,
- confirmText: Strings.Modals.close,
- onConfirm: () => {resolve(true);},
- onCancel: () => {resolve(false);}
- });
- });
- }
+
+
+ const finalClassname = `floating-window${` ${className}` || ""}${resizable ? " resizable" : ""}${modalOpen ? " modal-open" : ""}`;
+ const styles = {height: initialHeight, width: initialWidth, left: position.x || 0, top: position.y || 0};
+ return
;
}
\ No newline at end of file
diff --git a/renderer/src/ui/floatingwindows.js b/renderer/src/ui/floatingwindows.js
index 7ec2dd39..c6d1d1cd 100644
--- a/renderer/src/ui/floatingwindows.js
+++ b/renderer/src/ui/floatingwindows.js
@@ -1,25 +1,25 @@
-import {WebpackModules, React, ReactDOM, DOMManager} from "modules";
+import {WebpackModules, React, ReactDOM, DOMManager, Events} from "modules";
import FloatingWindowContainer from "./floating/container";
/* eslint-disable new-cap */
const AppLayerProvider = WebpackModules.getByDisplayName("AppLayerProvider");
+let hasInitialized = false;
export default class FloatingWindows {
static initialize() {
- const containerRef = React.createRef();
- const container =
;
+ const container =
;
const wrapped = AppLayerProvider
? React.createElement(AppLayerProvider().props.layerContext.Provider, {value: [document.querySelector("#app-mount > .layerContainer-2v_Sit")]}, container) // eslint-disable-line new-cap
: container;
const div = DOMManager.parseHTML(`
`);
DOMManager.bdBody.append(div);
ReactDOM.render(wrapped, div);
- this.ref = containerRef;
+ hasInitialized = true;
}
static open(window) {
- if (!this.ref) this.initialize();
- return this.ref.current.open(window);
+ if (!hasInitialized) this.initialize();
+ return Events.emit("open-window", window);
}
}
\ No newline at end of file
diff --git a/renderer/src/ui/icons/bdlogo.jsx b/renderer/src/ui/icons/bdlogo.jsx
index e10c56dd..d6dc74d1 100644
--- a/renderer/src/ui/icons/bdlogo.jsx
+++ b/renderer/src/ui/icons/bdlogo.jsx
@@ -1,11 +1,9 @@
import {React} from "modules";
-export default class BDLogo extends React.Component {
- render() {
- return
;
- }
-}
\ No newline at end of file
+export default function BDLogo(props) {
+ return
;
+}
diff --git a/renderer/src/ui/icons/check.jsx b/renderer/src/ui/icons/check.jsx
index c84fd0fc..d994ea89 100644
--- a/renderer/src/ui/icons/check.jsx
+++ b/renderer/src/ui/icons/check.jsx
@@ -1,11 +1,9 @@
import {React} from "modules";
-export default class Checkmark extends React.Component {
- render() {
- const size = this.props.size || "24px";
- return
;
- }
+export default function Checkmark(props) {
+ const size = props.size || "24px";
+ return
;
}
\ No newline at end of file
diff --git a/renderer/src/ui/icons/close.jsx b/renderer/src/ui/icons/close.jsx
index b93cba8e..55a45c0a 100644
--- a/renderer/src/ui/icons/close.jsx
+++ b/renderer/src/ui/icons/close.jsx
@@ -1,13 +1,11 @@
import {React} from "modules";
-export default class CloseButton extends React.Component {
- render() {
- const size = this.props.size || "18px";
- return
;
- }
+export default function Close(props) {
+ const size = props.size || "18px";
+ return
;
}
\ No newline at end of file
diff --git a/renderer/src/ui/icons/cog.jsx b/renderer/src/ui/icons/cog.jsx
index 4d76b92a..9a929bf5 100644
--- a/renderer/src/ui/icons/cog.jsx
+++ b/renderer/src/ui/icons/cog.jsx
@@ -1,11 +1,9 @@
import {React} from "modules";
-export default class Cog extends React.Component {
- render() {
- const size = this.props.size || "20px";
- return
;
- }
+export default function Cog(props) {
+ const size = props.size || "20px";
+ return
;
}
\ No newline at end of file
diff --git a/renderer/src/ui/icons/delete.jsx b/renderer/src/ui/icons/delete.jsx
index 4dcc5d16..92f4d79d 100644
--- a/renderer/src/ui/icons/delete.jsx
+++ b/renderer/src/ui/icons/delete.jsx
@@ -1,11 +1,9 @@
import {React} from "modules";
-export default class Delete extends React.Component {
- render() {
- const size = this.props.size || "24px";
- return
;
- }
+export default function Delete(props) {
+ const size = props.size || "24px";
+ return
;
}
\ No newline at end of file
diff --git a/renderer/src/ui/icons/detach.jsx b/renderer/src/ui/icons/detach.jsx
index e12449d9..8e5ade9f 100644
--- a/renderer/src/ui/icons/detach.jsx
+++ b/renderer/src/ui/icons/detach.jsx
@@ -1,11 +1,9 @@
import {React} from "modules";
-export default class Detach extends React.Component {
- render() {
- const size = this.props.size || "24px";
- return
;
- }
+export default function Detach(props) {
+ const size = props.size || "24px";
+ return
;
}
\ No newline at end of file
diff --git a/renderer/src/ui/icons/dollarsign.jsx b/renderer/src/ui/icons/dollarsign.jsx
index 3352af7a..5634e440 100644
--- a/renderer/src/ui/icons/dollarsign.jsx
+++ b/renderer/src/ui/icons/dollarsign.jsx
@@ -1,11 +1,9 @@
import {React} from "modules";
-export default class DollarSign extends React.Component {
- render() {
- const size = this.props.size || "18px";
- return