diff --git a/app/components/Button.tsx b/app/components/Button.tsx index 0aa6b15580..a3fed34ab5 100644 --- a/app/components/Button.tsx +++ b/app/components/Button.tsx @@ -5,6 +5,7 @@ import { twMerge } from "tailwind-merge"; export interface ButtonPropsI extends React.ButtonHTMLAttributes { icon?: IconDefinition; + iconFlip?: boolean; variant?: "primary" | "secondary" | "action"; } @@ -14,12 +15,13 @@ export const Button = ({ className: propsClassName, variant: propsVariant = "primary", disabled, + iconFlip = false, ...rest }: ButtonPropsI) => { function getVariantClassNames(variant: string) { switch (variant) { case "secondary": { - return " bg-brand-secondary hover:bg-brand-secondary-900 text-white focus-visible:outline-brand-secondary"; + return "bg-brand-secondary hover:bg-brand-secondary-900 text-white focus-visible:outline-brand-secondary"; } case "action": { return " bg-action hover:bg-action-500 text-white focus-visible:outline-action"; @@ -44,8 +46,9 @@ export const Button = ({ return ( ); }; diff --git a/app/components/ButtonDropdown.tsx b/app/components/ButtonDropdown.tsx new file mode 100644 index 0000000000..8dd6f7eddf --- /dev/null +++ b/app/components/ButtonDropdown.tsx @@ -0,0 +1,139 @@ +import { Fragment, useState } from "react"; +import { Menu, Transition } from "@headlessui/react"; +import { + IconDefinition, + faChevronDown, +} from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Button, ButtonPropsI } from "./Button"; +import { TransitionDialogue } from "~/components/TransitionDialogue"; + +type UnionedButtonProps = { label?: string } & ButtonPropsI; +type ModalPropsType = ButtonDropdownProps["options"][number]["dialogProps"]; + +interface ButtonDropdownProps { + label: string; + icon?: IconDefinition; + options: Array<{ + label: string; + description?: string; + onClick?: () => void; + disabled?: boolean; + divider?: boolean; + dialogProps?: { + title: string; + content: React.ReactNode; + className?: string; + confirmButtonProps?: UnionedButtonProps; + closeButtonProps?: UnionedButtonProps; + showClose?: boolean; + }; + }>; +} + +export const ButtonDropdown = ({ + label, + options, + icon, +}: ButtonDropdownProps) => { + const [isOpen, setIsOpen] = useState(false); + const [modalProps, setModalProps] = useState(null); + + const closeModal = () => setIsOpen(false); + + const handleOptionClick = (option) => { + if (option.onClick) { + option.onClick(); + } + if (option.dialogProps) { + setModalProps(option.dialogProps); + setIsOpen(true); + } + }; + + return ( +
+ + + + + + +
+ {options.map((option, index) => ( + + {option.divider && ( +
+ )} + + {({ active }) => ( + + )} + + + ))} +
+ + +
+ + {modalProps && ( + + {modalProps.content} + +
+ {modalProps.showClose !== false && modalProps.closeButtonProps && ( + + )} + + {modalProps.confirmButtonProps && ( + + )} +
+
+ )} +
+ ); +}; diff --git a/app/components/Input.tsx b/app/components/Input.tsx index d02871039c..3c2b234082 100644 --- a/app/components/Input.tsx +++ b/app/components/Input.tsx @@ -4,8 +4,11 @@ import { IconDefinition } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Label } from "./Typography"; import { kebabCase } from "~/utilities"; -import { disableHotkeyInput,enableHotkeyInput,DomLevels } from "~/pages/PageEnigma/store"; - +import { + disableHotkeyInput, + enableHotkeyInput, + DomLevels, +} from "~/pages/PageEnigma/store"; interface InputProps extends React.InputHTMLAttributes { label?: string; @@ -18,7 +21,7 @@ export const Input = React.forwardRef(({ ref: React.ForwardedRef ) => { return ( -
+
{label && }
diff --git a/app/components/index.tsx b/app/components/index.tsx index 96e86d7f31..a4c6192f2e 100644 --- a/app/components/index.tsx +++ b/app/components/index.tsx @@ -1,26 +1,40 @@ import { Button } from "./Button"; +import { ButtonDropdown } from "./ButtonDropdown"; import { ButtonIcon } from "./ButtonIcon"; import { ButtonLink } from "./ButtonLink"; import { ButtonIconSelect } from "./ButtonIconSelect"; import { Input } from "./Input"; import { InputVector } from "./InputVector"; -import { H1, H2, H3, H4, H5, H6, P, Label, Link} from "./Typography"; +import { H1, H2, H3, H4, H5, H6, P, Label, Link } from "./Typography"; import { ItemPicker } from "./ItemPicker"; import { Textarea } from "./Textarea"; import { TransitionDialogue } from "./TransitionDialogue"; -import { ListDropdown } from "./ListDropdown" -import { ListSearchDropdown } from "./ListSearchDropdown" +import { ListDropdown } from "./ListDropdown"; +import { ListSearchDropdown } from "./ListSearchDropdown"; import { LoadingBar } from "./LoadingBar"; -import { LoadingDots, LoadingDotsBricks, LoadingDotsTyping } from "./LoadingDots"; +import { + LoadingDots, + LoadingDotsBricks, + LoadingDotsTyping, +} from "./LoadingDots"; import { Pill } from "./Pill"; import { WaveformPlayer } from "./WaveformPlayer"; export { Button, + ButtonDropdown, ButtonIcon, ButtonLink, ButtonIconSelect, - H1,H2,H3,H4,H5,H6,P,Label,Link, + H1, + H2, + H3, + H4, + H5, + H6, + P, + Label, + Link, ItemPicker, Textarea, TransitionDialogue, diff --git a/app/pages/PageEnigma/comps/ControlsTopButtons/ControlsTopButtons.tsx b/app/pages/PageEnigma/comps/ControlsTopButtons/ControlsTopButtons.tsx index 2925797ad4..9f562c28c9 100644 --- a/app/pages/PageEnigma/comps/ControlsTopButtons/ControlsTopButtons.tsx +++ b/app/pages/PageEnigma/comps/ControlsTopButtons/ControlsTopButtons.tsx @@ -1,5 +1,5 @@ import { useContext, useState } from "react"; -import { Input } from "~/components"; +import { ButtonDropdown, Input } from "~/components"; import { ButtonDialogue } from "~/modules/ButtonDialogue"; import { EngineContext } from "../../../../contexts/EngineContext"; import { ToasterContext } from "~/contexts/ToasterContext"; @@ -7,6 +7,7 @@ import { ToasterContext } from "~/contexts/ToasterContext"; import { TestFeaturesButtons } from "./TestFeaturesButtons"; import { Help } from "./Help"; +import { faFile } from "@fortawesome/pro-solid-svg-icons"; export const ControlsTopButtons = () => { const editorEngine = useContext(EngineContext); @@ -39,44 +40,112 @@ export const ControlsTopButtons = () => { return (
-
- - { - setSceneToken(e.target.value); - }} - /> - +
+ { + setSceneToken(e.target.value); + }} + /> + ), + confirmButtonProps: { + label: "Create", + disabled: sceneName === "", + onClick: () => console.log("NEW SCENE"), + }, + closeButtonProps: { + label: "Cancel", + }, + showClose: true, + }, + }, + { + label: "Load existing scene", + description: "Ctrl+O", + dialogProps: { + title: "Load a Scene", + content: ( + { + setSceneToken(e.target.value); + }} + /> + ), + confirmButtonProps: { + label: "Load", + disabled: sceneToken === "", + onClick: handleButtonLoadScene, + }, + closeButtonProps: { + label: "Cancel", + }, + showClose: true, + }, + }, - - { - setSceneName(e.target.value); - }} - /> - + { + label: "Save scene", + description: "Ctrl+S", + dialogProps: { + title: "Save Scene", + content: ( + { + setSceneName(e.target.value); + }} + /> + ), + confirmButtonProps: { + label: "Save", + disabled: sceneName === "", + onClick: handleButtonSave, + }, + closeButtonProps: { + label: "Cancel", + }, + showClose: true, + }, + divider: true, + }, + + { + label: "Save scene as copy", + description: "Ctrl+Shift+S", + dialogProps: { + title: "Save Scene as Copy", + content: ( + { + setSceneName(e.target.value); + }} + /> + ), + confirmButtonProps: { + label: "Save", + disabled: sceneName === "", + onClick: handleButtonSave, + }, + closeButtonProps: { + label: "Cancel", + }, + showClose: true, + }, + }, + ]} + />