Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a programmatic way to close the modal window #85

Open
erickskrauch opened this issue Nov 18, 2024 · 3 comments
Open

Provide a programmatic way to close the modal window #85

erickskrauch opened this issue Nov 18, 2024 · 3 comments

Comments

@erickskrauch
Copy link

My modal does submit some action and I want it to close itself after the action's Promise is resolved. There are 2 ways to open a modal: by directly passing the open prop or by trigger element. Inside you can use the Modal.Close component to close the modal in its native way. But there is no other way. Also, there is no forwardRef on Modal.Close, so I can't trigger a click event on the element. So I ended up with the following code:

const MyCustomModal: FC<Props> = ({ onSubmit, onOpenChange, ...props }) => {
    const [ isLoading, setIsLoading ] = useState<boolean>(false);

    const onClick = useCallback(async () => {
        setIsLoading(true);
        try {
            await onSubmit?.();
        } finally {
            setIsLoading(false);
        }

        // There is no way to close a modal natively, honoring all events, from within, other than using Modal.Close.
        // I wanted to use a programmatic click on the ref of the element, but unfortunately this solution isn't
        // possible because the component doesn't have a forwardRef.
        //
        // So the current implementation just fires the native event and relies on the fact that the modal window
        // being displayed via `open`/`onOpenChange` rather than a `trigger`.
        onOpenChange?.(false);
    }, [onOpenChange]);

    return (
        <Modal onOpenChange={onOpenChange} {...props}>
            <ButtonCell mode="destructive" disabled={isLoading} onClick={onClick}>
                Delete
            </ButtonCell>
        </Modal>
    );
};

So it would be nice to provide some imperative property on the Modal component, that will let the developer close the modal in its native way regardless of the way it was opened.

@mainsmirnov
Copy link
Collaborator

Hey, there is example in the docs how to do it, please refer to it. It works as expected

https://tgui.xelene.me/?path=/docs/overlays-modal--documentation#controlled

export const Controlled: Story = {
  args: {
    ...Playground.args,
  },
  render: (args) => {
    const [isOpen, setIsOpen] = useState(args.open);
    const [isFetching, setIsFetching] = useState(false);

    const fetchAndClose = () => {
      setIsFetching(true);
      setTimeout(() => {
        setIsFetching(false);
        setIsOpen(false);
      }, 1000);
    };

    return (
      <Placeholder
        header="This modal will be closed after 1000ms, click fetch"
        description="Click fetch"
        action={<Button size="m" onClick={() => setIsOpen(true)}>Open again</Button>}
      >
        <Modal
          {...args}
          trigger={undefined}
          open={isOpen}
          onOpenChange={setIsOpen}
        >
          <Placeholder action={(
            <Button
              size="m"
              loading={isFetching}
              onClick={fetchAndClose}
            >
              Fetch data and close
            </Button>
          )} />
        </Modal>
      </Placeholder>
    );
  },
  decorators: [DecoratorFullScreen],
};

@erickskrauch
Copy link
Author

This example shows how to close the modal by the parent component, while I'm asking how to close it by itself. You might say that this is not "react-way", but why is there a Modal.Close component that directly allows closing the modal from within?

@mainsmirnov
Copy link
Collaborator

If you want to close the modal in any custom way, you need to use the open prop and manage the state as you want. States can be updated from any child component/you can use the store. Otherwise, I don't understand your use case

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants