UNPKG

@react95/core

Version:
176 lines (175 loc) 4.83 kB
import { nanoid } from "nanoid"; import React, { useState, useEffect, useRef, useImperativeHandle } from "react"; import { useDraggable } from "@neodrag/react"; import { Button } from "../Button/Button.mjs"; import { fixedForwardRef, Frame } from "../Frame/Frame.mjs"; import { TitleBar } from "../TitleBar/TitleBar.mjs"; import { content, modalWrapper, menuWrapper, menuItem, buttonWrapper } from "./Modal.css.mjs"; import cn from "classnames"; import { useOnClickOutside } from "usehooks-ts"; import { useModal } from "../shared/use-modal.mjs"; import { ModalEvents } from "../shared/modal-types.mjs"; const ModalContent = fixedForwardRef( (rest, ref) => /* @__PURE__ */ React.createElement(Frame, { ...rest, ref, className: cn(content, rest.className) }) ); const ModalMinimize = fixedForwardRef( (props, ref) => { const [id, setId] = useState(""); const { minimize, focus, subscribe } = useModal(); useEffect(() => { const handleVisibilityChange = ({ id: activeId }) => { setId(activeId); }; const unsubscribe = subscribe( ModalEvents.ModalVisibilityChanged, handleVisibilityChange ); return unsubscribe; }, [subscribe]); const handleMinimize = () => { minimize(id); focus("no-id"); }; return /* @__PURE__ */ React.createElement(TitleBar.Minimize, { ...props, ref, onClick: handleMinimize }); } ); const ModalRenderer = ({ id: providedId, hasWindowButton: hasButton = true, buttons = [], buttonsAlignment = "flex-end", children, icon, menu = [], title, dragOptions, titleBarOptions, className, ...rest }, ref) => { const [id] = useState(providedId || nanoid()); const [menuOpened, setMenuOpened] = useState(""); const [isActive, setIsActive] = useState(false); const [isModalMinimized, setIsModalMinimized] = useState(false); const { add, remove, focus, subscribe } = useModal(); const draggableRef = useRef(null); useDraggable(draggableRef, { ...dragOptions, handle: ".draggable" }); const menuRef = useRef(null); useOnClickOutside(menuRef, () => { setMenuOpened(""); }); useEffect(() => { add({ icon, title: title || "", id, hasButton }); const unsubscribeVisibility = subscribe( ModalEvents.ModalVisibilityChanged, ({ id: activeId }) => { setIsActive(activeId === id); } ); focus(id); return () => { remove(id); unsubscribeVisibility(); }; }, [id, icon, title, hasButton, providedId, add, remove, focus, subscribe]); useEffect(() => { const unsubscribeMinimize = subscribe( ModalEvents.MinimizeModal, ({ id: activeId }) => { if (activeId === id) { setIsModalMinimized(true); } } ); const unsubscribeRestore = subscribe( ModalEvents.RestoreModal, ({ id: activeId }) => { if (activeId === id) { setIsModalMinimized(false); } } ); return () => { unsubscribeMinimize(); unsubscribeRestore(); }; }, [id, subscribe]); useImperativeHandle(ref, () => { return draggableRef.current; }); return /* @__PURE__ */ React.createElement( Frame, { ...rest, className: cn( modalWrapper({ active: isActive, minimized: isModalMinimized }), className ), role: "dialog", "aria-hidden": isModalMinimized, ref: draggableRef, onMouseDown: () => { focus(id); } }, /* @__PURE__ */ React.createElement( TitleBar, { active: isActive, icon, title, className: "draggable", mb: "$2" }, titleBarOptions && /* @__PURE__ */ React.createElement(TitleBar.OptionsBox, null, titleBarOptions) ), menu && menu.length > 0 && /* @__PURE__ */ React.createElement("ul", { className: menuWrapper, ref: menuRef }, menu.map(({ name, list }) => { const active = menuOpened === name; return /* @__PURE__ */ React.createElement( "li", { key: name, onMouseDown: () => setMenuOpened(name), className: menuItem({ active }) }, name, active && list ); })), children, buttons && buttons.length > 0 && /* @__PURE__ */ React.createElement( Frame, { className: buttonWrapper, justifyContent: buttonsAlignment }, buttons.map((button) => /* @__PURE__ */ React.createElement( Button, { key: button.value, onClick: button.onClick, value: button.value }, button.value )) ) ); }; const Modal = Object.assign( fixedForwardRef(ModalRenderer), { Content: ModalContent, Minimize: ModalMinimize } ); export { Modal };