diff --git a/docs/data/toolpad/core/components/use-dialogs/AlertDialog.js b/docs/data/toolpad/core/components/use-dialogs/AlertDialog.js
new file mode 100644
index 00000000000..a82e0bfb66b
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/AlertDialog.js
@@ -0,0 +1,28 @@
+import * as React from 'react';
+import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ return (
+
+
+
+ );
+}
+
+export default function AlertDialog() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/AlertDialog.tsx b/docs/data/toolpad/core/components/use-dialogs/AlertDialog.tsx
new file mode 100644
index 00000000000..a82e0bfb66b
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/AlertDialog.tsx
@@ -0,0 +1,28 @@
+import * as React from 'react';
+import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ return (
+
+
+
+ );
+}
+
+export default function AlertDialog() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/AlertDialog.tsx.preview b/docs/data/toolpad/core/components/use-dialogs/AlertDialog.tsx.preview
new file mode 100644
index 00000000000..4f81c7d194c
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/AlertDialog.tsx.preview
@@ -0,0 +1 @@
+await dialogs.alert('Hello World');
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/use-dialogs/ConfirmDialog.js b/docs/data/toolpad/core/components/use-dialogs/ConfirmDialog.js
new file mode 100644
index 00000000000..d28e8a7ddfa
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/ConfirmDialog.js
@@ -0,0 +1,36 @@
+import * as React from 'react';
+import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ return (
+
+
+
+ );
+}
+
+export default function ConfirmDialog() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/ConfirmDialog.tsx b/docs/data/toolpad/core/components/use-dialogs/ConfirmDialog.tsx
new file mode 100644
index 00000000000..d28e8a7ddfa
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/ConfirmDialog.tsx
@@ -0,0 +1,36 @@
+import * as React from 'react';
+import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ return (
+
+
+
+ );
+}
+
+export default function ConfirmDialog() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/ConfirmDialog.tsx.preview b/docs/data/toolpad/core/components/use-dialogs/ConfirmDialog.tsx.preview
new file mode 100644
index 00000000000..9500e780731
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/ConfirmDialog.tsx.preview
@@ -0,0 +1,9 @@
+const confirmed = await dialogs.confirm('Are you sure?', {
+ okText: 'Yes',
+ cancelText: 'No',
+});
+if (confirmed) {
+ await dialogs.alert("Then let's do it!");
+} else {
+ await dialogs.alert('Ok, forget about it!');
+}
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/use-dialogs/CustomDialog.js b/docs/data/toolpad/core/components/use-dialogs/CustomDialog.js
new file mode 100644
index 00000000000..cd1b47cd5a8
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/CustomDialog.js
@@ -0,0 +1,61 @@
+import * as React from 'react';
+import PropTypes from 'prop-types';
+import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import DialogActions from '@mui/material/DialogActions';
+
+function MyCustomDialog({ open, onClose }) {
+ return (
+
+ );
+}
+
+MyCustomDialog.propTypes = {
+ /**
+ * A function to call when the dialog should be closed. If the dialog has a return
+ * value, it should be passed as an argument to this function. You should use the promise
+ * that is returned to show a loading state while the dialog is performing async actions
+ * on close.
+ * @param result The result to return from the dialog.
+ * @returns A promise that resolves when the dialog can be fully closed.
+ */
+ onClose: PropTypes.func.isRequired,
+ /**
+ * Whether the dialog is open.
+ */
+ open: PropTypes.bool.isRequired,
+};
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ return (
+
+
+
+ );
+}
+
+export default function CustomDialog() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/CustomDialog.tsx b/docs/data/toolpad/core/components/use-dialogs/CustomDialog.tsx
new file mode 100644
index 00000000000..dde8f93c14d
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/CustomDialog.tsx
@@ -0,0 +1,44 @@
+import * as React from 'react';
+import { DialogsProvider, useDialogs, DialogProps } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import DialogActions from '@mui/material/DialogActions';
+
+function MyCustomDialog({ open, onClose }: DialogProps) {
+ return (
+
+ );
+}
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ return (
+
+
+
+ );
+}
+
+export default function CustomDialog() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/CustomDialog.tsx.preview b/docs/data/toolpad/core/components/use-dialogs/CustomDialog.tsx.preview
new file mode 100644
index 00000000000..3c31a5c7950
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/CustomDialog.tsx.preview
@@ -0,0 +1 @@
+await dialogs.open(MyCustomDialog);
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayload.js b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayload.js
new file mode 100644
index 00000000000..c53ce5083e7
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayload.js
@@ -0,0 +1,73 @@
+import * as React from 'react';
+import PropTypes from 'prop-types';
+import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import DialogActions from '@mui/material/DialogActions';
+import TextField from '@mui/material/TextField';
+import Stack from '@mui/material/Stack';
+
+function MyCustomDialog({ payload, open, onClose }) {
+ return (
+
+ );
+}
+
+MyCustomDialog.propTypes = {
+ /**
+ * A function to call when the dialog should be closed. If the dialog has a return
+ * value, it should be passed as an argument to this function. You should use the promise
+ * that is returned to show a loading state while the dialog is performing async actions
+ * on close.
+ * @param result The result to return from the dialog.
+ * @returns A promise that resolves when the dialog can be fully closed.
+ */
+ onClose: PropTypes.func.isRequired,
+ /**
+ * Whether the dialog is open.
+ */
+ open: PropTypes.bool.isRequired,
+ /**
+ * The payload that was passed when the dialog was opened.
+ */
+ payload: PropTypes.string.isRequired,
+};
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ const [payload, setPayload] = React.useState('Some payload');
+ return (
+
+ setPayload(event.currentTarget.value)}
+ />
+
+
+ );
+}
+
+export default function CustomDialogWithPayload() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayload.tsx b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayload.tsx
new file mode 100644
index 00000000000..7705547d2ca
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayload.tsx
@@ -0,0 +1,52 @@
+import * as React from 'react';
+import { DialogsProvider, useDialogs, DialogProps } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import DialogActions from '@mui/material/DialogActions';
+import TextField from '@mui/material/TextField';
+import Stack from '@mui/material/Stack';
+
+function MyCustomDialog({ payload, open, onClose }: DialogProps) {
+ return (
+
+ );
+}
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ const [payload, setPayload] = React.useState('Some payload');
+ return (
+
+ setPayload(event.currentTarget.value)}
+ />
+
+
+ );
+}
+
+export default function CustomDialogWithPayload() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayload.tsx.preview b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayload.tsx.preview
new file mode 100644
index 00000000000..1a23cecf043
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithPayload.tsx.preview
@@ -0,0 +1 @@
+await dialogs.open(MyCustomDialog, payload);
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithResult.js b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithResult.js
new file mode 100644
index 00000000000..f224de43d21
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithResult.js
@@ -0,0 +1,74 @@
+import * as React from 'react';
+import PropTypes from 'prop-types';
+import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import DialogActions from '@mui/material/DialogActions';
+import TextField from '@mui/material/TextField';
+import Stack from '@mui/material/Stack';
+import DialogContentText from '@mui/material/DialogContentText';
+
+function MyCustomDialog({ open, onClose }) {
+ const [result, setResult] = React.useState('Jon Snow');
+ return (
+
+ );
+}
+
+MyCustomDialog.propTypes = {
+ /**
+ * A function to call when the dialog should be closed. If the dialog has a return
+ * value, it should be passed as an argument to this function. You should use the promise
+ * that is returned to show a loading state while the dialog is performing async actions
+ * on close.
+ * @param result The result to return from the dialog.
+ * @returns A promise that resolves when the dialog can be fully closed.
+ */
+ onClose: PropTypes.func.isRequired,
+ /**
+ * Whether the dialog is open.
+ */
+ open: PropTypes.bool.isRequired,
+};
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ return (
+
+
+
+ );
+}
+
+export default function CustomDialogWithResult() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithResult.tsx b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithResult.tsx
new file mode 100644
index 00000000000..518095645d7
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithResult.tsx
@@ -0,0 +1,57 @@
+import * as React from 'react';
+import { DialogsProvider, useDialogs, DialogProps } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import DialogActions from '@mui/material/DialogActions';
+import TextField from '@mui/material/TextField';
+import Stack from '@mui/material/Stack';
+import DialogContentText from '@mui/material/DialogContentText';
+
+function MyCustomDialog({ open, onClose }: DialogProps) {
+ const [result, setResult] = React.useState('Jon Snow');
+ return (
+
+ );
+}
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ return (
+
+
+
+ );
+}
+
+export default function CustomDialogWithResult() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithResult.tsx.preview b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithResult.tsx.preview
new file mode 100644
index 00000000000..7a20a35ab3e
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/CustomDialogWithResult.tsx.preview
@@ -0,0 +1,2 @@
+const result = await dialogs.open(MyCustomDialog);
+await dialogs.alert(`Your name is "${result}"`);
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/use-dialogs/PromptDialog.js b/docs/data/toolpad/core/components/use-dialogs/PromptDialog.js
new file mode 100644
index 00000000000..36977fbc84b
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/PromptDialog.js
@@ -0,0 +1,31 @@
+import * as React from 'react';
+import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ return (
+
+
+
+ );
+}
+
+export default function PromptDialog() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/PromptDialog.tsx b/docs/data/toolpad/core/components/use-dialogs/PromptDialog.tsx
new file mode 100644
index 00000000000..36977fbc84b
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/PromptDialog.tsx
@@ -0,0 +1,31 @@
+import * as React from 'react';
+import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ return (
+
+
+
+ );
+}
+
+export default function PromptDialog() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/PromptDialog.tsx.preview b/docs/data/toolpad/core/components/use-dialogs/PromptDialog.tsx.preview
new file mode 100644
index 00000000000..b20e0553547
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/PromptDialog.tsx.preview
@@ -0,0 +1,4 @@
+const name = await dialogs.prompt("What's your name?");
+if (name) {
+ await dialogs.alert(`Hi there, ${name}`);
+}
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/use-dialogs/StackedDialog.js b/docs/data/toolpad/core/components/use-dialogs/StackedDialog.js
new file mode 100644
index 00000000000..86aec23eb80
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/StackedDialog.js
@@ -0,0 +1,74 @@
+import * as React from 'react';
+import PropTypes from 'prop-types';
+import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import DialogActions from '@mui/material/DialogActions';
+
+function MyCustomDialog({ payload, open, onClose }) {
+ const dialogs = useDialogs();
+ return (
+
+ );
+}
+
+MyCustomDialog.propTypes = {
+ /**
+ * A function to call when the dialog should be closed. If the dialog has a return
+ * value, it should be passed as an argument to this function. You should use the promise
+ * that is returned to show a loading state while the dialog is performing async actions
+ * on close.
+ * @param result The result to return from the dialog.
+ * @returns A promise that resolves when the dialog can be fully closed.
+ */
+ onClose: PropTypes.func.isRequired,
+ /**
+ * Whether the dialog is open.
+ */
+ open: PropTypes.bool.isRequired,
+ /**
+ * The payload that was passed when the dialog was opened.
+ */
+ payload: PropTypes.number.isRequired,
+};
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ return (
+
+
+
+ );
+}
+
+export default function StackedDialog() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/StackedDialog.tsx b/docs/data/toolpad/core/components/use-dialogs/StackedDialog.tsx
new file mode 100644
index 00000000000..74cac321345
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/StackedDialog.tsx
@@ -0,0 +1,53 @@
+import * as React from 'react';
+import { DialogsProvider, useDialogs, DialogProps } from '@toolpad/core/useDialogs';
+import Button from '@mui/material/Button';
+import Dialog from '@mui/material/Dialog';
+import DialogTitle from '@mui/material/DialogTitle';
+import DialogContent from '@mui/material/DialogContent';
+import DialogActions from '@mui/material/DialogActions';
+
+function MyCustomDialog({ payload, open, onClose }: DialogProps) {
+ const dialogs = useDialogs();
+ return (
+
+ );
+}
+
+function DemoContent() {
+ const dialogs = useDialogs();
+ return (
+
+
+
+ );
+}
+
+export default function StackedDialog() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-dialogs/StackedDialog.tsx.preview b/docs/data/toolpad/core/components/use-dialogs/StackedDialog.tsx.preview
new file mode 100644
index 00000000000..45e7f84f761
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/StackedDialog.tsx.preview
@@ -0,0 +1 @@
+await dialogs.open(MyCustomDialog, payload + 1);
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/use-dialogs/use-dialogs-api.md b/docs/data/toolpad/core/components/use-dialogs/use-dialogs-api.md
new file mode 100644
index 00000000000..de5b34176ac
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/use-dialogs-api.md
@@ -0,0 +1,176 @@
+# useDialogs API
+
+
API reference for the useDialogs hook.
+
+:::success
+For examples and details on the usage of this React hook, visit the demo pages:
+
+- [useDialogs](/toolpad/core/react-use-dialogs/)
+
+:::
+
+## Import
+
+```js
+import useDialogs from '@toolpad/core/useDialogs';
+// or
+import { useDialogs } from '@toolpad/core';
+```
+
+Learn about the difference by reading this [guide](https://mui.com/material-ui/guides/minimizing-bundle-size/) on minimizing bundle size.
+
+## Usage
+
+Get access to the dialogs API by invoking the hook.
+
+```js
+const dialogs = useDialogs();
+```
+
+## Reference
+
+### `dialogs.alert`
+
+Open an alert dialog, returns a promise that resolves when the user closes the dialog.
+
+```js
+await dialogs.alert('Something went wrong', {
+ title: 'Attention!',
+});
+```
+
+**Signature:**
+
+```js
+function alert(message, options?)
+```
+
+**Parameters:**
+
+- `message`: `React.ReactNode` The message to display in the alert dialog.
+- `options?`: `object` Extra configuration for the alert dialog.
+ - `onClose?`: `() => Promise` A function that is called before closing the dialog closes. The dialog stays open as long as the returned promise is not resolved. Use this if you want to perform an async action on close and show a loading state.
+ - `title?`: `React.ReactNode` A title for the dialog. Defaults to `'Alert'`.
+ - `okText?`: `React.ReactNode` The text to show in the "Ok" button. Defaults to `'Ok'`.
+
+**Returns:**
+
+`Promise` A promise that resolves once the user has dismissed the dialog.
+
+### `dialogs.confirm`
+
+Open a confirmation dialog. Returns a promise that resolves to true if the user confirms, false if the user cancels.
+
+```js
+const confirmed = await dialogs.confirm('Are you sure?', {
+ okText: 'Yes',
+ cancelText: 'No',
+});
+```
+
+**Signature:**
+
+```js
+function confirm(message, options?)
+```
+
+**Parameters:**
+
+- `message`: `React.ReactNode` The message to display in the confirmation dialog.
+- `options?`: `object` Extra configuration for the confirmation dialog.
+ - `onClose?`: `(result: boolean) => Promise` A function that is called before closing the dialog closes. The dialog stays open as long as the returned promise is not resolved. Use this if you want to perform an async action on close and show a loading state.
+ - `title?`: `React.ReactNode` A title for the dialog. Defaults to `'Confirm'`.
+ - `okText?`: `React.ReactNode` The text to show in the "Ok" button. Defaults to `'Ok'`.
+ - `severity?`: `'error' | 'info' | 'success' | 'warning'` Denotes the purpose of the dialog. This will affect the color of the "Ok" button. Defaults to `undefined`.
+ - `cancelText?`: `React.ReactNode` The text to show in the "Cancel" button. Defaults to `'Cancel'`.
+
+**Returns:**
+
+`Promise` A promise that resolves to true if the user confirms, false if the user cancels.
+
+### `dialogs.prompt`
+
+Open a prompt dialog to request user input. Returns a promise that resolves to the input if the user confirms, null if the user cancels.
+
+```js
+const input = await dialogs.prompt('What is your name?', {
+ cancelText: 'Leave me alone',
+});
+```
+
+**Signature:**
+
+```js
+function prompt(message, options?)
+```
+
+**Parameters:**
+
+- `message`: `React.ReactNode` The message to display in the prompt dialog.
+- `options?`: `object` Extra configuration for the prompt dialog.
+ - `onClose?`: `(result: string) => Promise` A function that is called before closing the dialog closes. The dialog stays open as long as the returned promise is not resolved. Use this if you want to perform an async action on close and show a loading state.
+ - `title?`: `React.ReactNode` A title for the dialog. Defaults to `'Prompt'`.
+ - `okText?`: `React.ReactNode` The text to show in the "Ok" button. Defaults to `'Ok'`.
+ - `cancelText?`: `React.ReactNode` The text to show in the "Cancel" button. Defaults to `'Cancel'`.
+
+**Returns:**
+
+`Promise` A promise that resolves to the user input if the user confirms, null if the user cancels.
+
+### `dialogs.open`
+
+Open a custom dialog. The dialog is a React component that optionally takes a payload and optionally returns a result in its `onClose` property.
+
+```js
+function MyDialog({ open, onClose, payload }) {
+ // ...
+}
+
+const result = await dialogs.open(MyDialog, 123, {
+ onClose: async (result) => callApi(result),
+});
+```
+
+**Signature:**
+
+```js
+function open(component, payload, options?)
+```
+
+**Parameters:**
+
+- `component`: `React.ComponentType<{ open: boolean, onClose: (result: R) => Promise, payload: P }>` The dialog component to display.
+- `payload?`: `P` The optional payload passed to the dialog. Useful if you want to parametrize the dialog, or use instance specific data. This value will be kept constant during the lifetime of the dialog.
+- `options?`: `object` Extra configuration for the dialog.
+ - `onClose?`: `(result: R) => Promise` A function that is called before closing the dialog closes. The dialog stays open as long as the returned promise is not resolved. Use this if you want to perform an async action on close and show a loading state.
+
+**Returns:**
+
+`Promise` A promise that resolves to the user input if the user confirms, null if the user cancels.
+
+### `dialogs.close`
+
+Programmatically close a dialog that was previously opened with `dialogs.open`. If the dialog returns a result, `close` must also be called with a result. The original dialog promise will be resolved with this result. This promise is also returned from the `close` function.
+
+```js
+const myDialog = dialogs.open(/* ... */);
+
+// ...
+
+const result = await dialogs.close(myDialog, 123);
+```
+
+**Signature:**
+
+```js
+function close(dialog, result)
+```
+
+**Parameters:**
+
+- `dialog`: `Promise` The dialog to close. This should be a promise that was previously returned by `dialogs.open`.
+- `result?`: `R` The result to return from the dialog.
+
+**Returns:**
+
+`Promise` A promise that resolves with the dialog result when the dialog is fully closed.
diff --git a/docs/data/toolpad/core/components/use-dialogs/use-dialogs.md b/docs/data/toolpad/core/components/use-dialogs/use-dialogs.md
new file mode 100644
index 00000000000..703134587a2
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-dialogs/use-dialogs.md
@@ -0,0 +1,104 @@
+---
+productId: toolpad-core
+title: useDialogs
+components: DialogsProvider
+---
+
+# Dialogs
+
+
Imperative APIs to open and interact with dialogs.
+
+Toolpad core offers a set of abstractions that makes interacting with dialogs simpler. It has an imperative API to open and close dialogs, and allows dialogs to be stacked on top of each other.
+
+First thing you need to do is install the DialogsProvider at the root of your application.
+
+```tsx
+import { DialogsProvider } from '@toolpad/core/useDialogs';
+
+function App({ children }) {
+ return {children};
+}
+```
+
+To get access to the dialogs API you first have to call the `useDialogs` hook.
+
+```js
+import { useDialogs } from '@toolpad/core/useDialogs';
+
+function MyApp() {
+ const dialogs = useDialogs();
+ // ...
+}
+```
+
+:::info
+The Toolpad AppProvider automatically comes with dialogs enabled. You won't need to explicitly add the DialogsProvider in Toolpad applications.
+:::
+
+## Basic dialog
+
+Dialogs are React components that taken `open` and `onClose` properties and return a Dialog component. The `open` property reflects the open state of the dialog and you can call the `onClose` handler to close it.
+
+```js
+function MyCustomDialog({ open, onClose }: DialogProps) {
+ return (
+
+ );
+}
+```
+
+Now you can call the `dialogs.open` function and pass the component as a first parameter.
+
+{{"demo": "CustomDialog.js"}}
+
+## With dialog payload
+
+You can pass a `payload` to the dialog with the second parameter. The payload stays constant for the lifetime of the dialog.
+
+{{"demo": "CustomDialogWithPayload.js"}}
+
+## With dialog result
+
+A dialog can return a value with the `onClose` handler. The promise returned by the `open` method is resolved with the value that was passed to `onClose`.
+
+{{"demo": "CustomDialogWithResult.js"}}
+
+## Stacked dialogs
+
+Dialogs can be stacked. A dialog can open other another dialog which comes to the foreground upon opening. Closing the latter reveals the former again.
+
+{{"demo": "StackedDialog.js"}}
+
+## System dialogs
+
+Toolpad comes with a set of system dialogs that improve on the native `window.alert`, `window.confirm`, and `window.prompt` APIs. These APIs are very similar, but they create dialogs that follow your application theme.
+
+### Alert
+
+Analog to [`window.alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert) it opens a dialog with a message for the user. The only action to be taken is to acknowledge the message after which the dialog closes.
+The dialog title and button text are customizable with the `title` and `okText` properties.
+
+{{"demo": "AlertDialog.js"}}
+
+### Confirm
+
+Analog to [`window.confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm) it opens a dialog with a question for the user. The user can either confirm or cancel and the dialog result is a boolean which is `true` when the user confirmed.
+The dialog title and button texts are customizable with the `title`, `okText`, and `cancelText` properties.
+
+{{"demo": "ConfirmDialog.js"}}
+
+### Prompt
+
+Analog to [`window.prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) it opens a dialog inquiring the user for some input text. The user can fill the input box and upon confirmation the promise returned from the `prompt` call is resolved with its value. The dialog title and button texts are customizable with the `title`, `okText`, and `cancelText` properties.
+
+{{"demo": "PromptDialog.js"}}
+
+## Hook API
+
+- [`useDialogs()`](/toolpad/core/react-use-dialogs/api/)
diff --git a/docs/data/toolpad/core/components/use-notifications/AlertNotification.js b/docs/data/toolpad/core/components/use-notifications/AlertNotification.js
new file mode 100644
index 00000000000..06da677b18d
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/AlertNotification.js
@@ -0,0 +1,47 @@
+import * as React from 'react';
+import { useNotifications } from '@toolpad/core/useNotifications';
+import Button from '@mui/material/Button';
+import FormControlLabel from '@mui/material/FormControlLabel';
+import FormControl from '@mui/material/FormControl';
+import FormLabel from '@mui/material/FormLabel';
+import RadioGroup from '@mui/material/RadioGroup';
+import Radio from '@mui/material/Radio';
+import Stack from '@mui/material/Stack';
+
+export default function AlertNotification() {
+ const notifications = useNotifications();
+ const [severity, setSeverity] = React.useState('info');
+ return (
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-notifications/CloseNotification.tsx.preview b/docs/data/toolpad/core/components/use-notifications/CloseNotification.tsx.preview
new file mode 100644
index 00000000000..9d1eb56a00e
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/CloseNotification.tsx.preview
@@ -0,0 +1,12 @@
+const key = online
+ ? notifications.show('You are now online', {
+ severity: 'success',
+ autoHideDuration: 3000,
+ })
+ : notifications.show('You are now offline', {
+ severity: 'error',
+ });
+
+return () => {
+ notifications.close(key);
+};
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/use-notifications/DedupeNotification.js b/docs/data/toolpad/core/components/use-notifications/DedupeNotification.js
new file mode 100644
index 00000000000..ba06386bf58
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/DedupeNotification.js
@@ -0,0 +1,24 @@
+import * as React from 'react';
+import { useNotifications } from '@toolpad/core/useNotifications';
+import Button from '@mui/material/Button';
+
+export default function DedupeNotification() {
+ const notifications = useNotifications();
+ return (
+ // preview
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-notifications/DedupeNotification.tsx b/docs/data/toolpad/core/components/use-notifications/DedupeNotification.tsx
new file mode 100644
index 00000000000..ba06386bf58
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/DedupeNotification.tsx
@@ -0,0 +1,24 @@
+import * as React from 'react';
+import { useNotifications } from '@toolpad/core/useNotifications';
+import Button from '@mui/material/Button';
+
+export default function DedupeNotification() {
+ const notifications = useNotifications();
+ return (
+ // preview
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-notifications/DedupeNotification.tsx.preview b/docs/data/toolpad/core/components/use-notifications/DedupeNotification.tsx.preview
new file mode 100644
index 00000000000..da00866cded
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/DedupeNotification.tsx.preview
@@ -0,0 +1,4 @@
+notifications.show('Listen carefully, I will say this only once', {
+ key: 'dedupe-notification',
+ autoHideDuration: 5000,
+});
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/use-notifications/MultipleNotifications.js b/docs/data/toolpad/core/components/use-notifications/MultipleNotifications.js
new file mode 100644
index 00000000000..86d7a74e846
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/MultipleNotifications.js
@@ -0,0 +1,21 @@
+import * as React from 'react';
+import { useNotifications } from '@toolpad/core/useNotifications';
+import Button from '@mui/material/Button';
+
+export default function MultipleNotifications() {
+ const notifications = useNotifications();
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-notifications/MultipleNotifications.tsx b/docs/data/toolpad/core/components/use-notifications/MultipleNotifications.tsx
new file mode 100644
index 00000000000..86d7a74e846
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/MultipleNotifications.tsx
@@ -0,0 +1,21 @@
+import * as React from 'react';
+import { useNotifications } from '@toolpad/core/useNotifications';
+import Button from '@mui/material/Button';
+
+export default function MultipleNotifications() {
+ const notifications = useNotifications();
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-notifications/MultipleNotifications.tsx.preview b/docs/data/toolpad/core/components/use-notifications/MultipleNotifications.tsx.preview
new file mode 100644
index 00000000000..6e82e81e512
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/MultipleNotifications.tsx.preview
@@ -0,0 +1,2 @@
+notifications.show('Hello', { autoHideDuration: 1000 });
+notifications.show('Goodbye', { autoHideDuration: 1000 });
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/use-notifications/ScopedNotification.js b/docs/data/toolpad/core/components/use-notifications/ScopedNotification.js
new file mode 100644
index 00000000000..0116e791617
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/ScopedNotification.js
@@ -0,0 +1,38 @@
+import * as React from 'react';
+import {
+ NotificationsProvider,
+ useNotifications,
+} from '@toolpad/core/useNotifications';
+import Button from '@mui/material/Button';
+import { Box, Snackbar, styled } from '@mui/material';
+
+const notificationsProviderSlots = {
+ snackbar: styled(Snackbar)({ position: 'absolute' }),
+};
+
+function ScopedContent() {
+ const notifications = useNotifications();
+ return (
+
+
+
+ );
+}
+
+export default function ScopedNotification() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-notifications/ScopedNotification.tsx b/docs/data/toolpad/core/components/use-notifications/ScopedNotification.tsx
new file mode 100644
index 00000000000..e10db7591a5
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/ScopedNotification.tsx
@@ -0,0 +1,39 @@
+import * as React from 'react';
+import {
+ NotificationsProvider,
+ useNotifications,
+ NotificationsProviderSlots,
+} from '@toolpad/core/useNotifications';
+import Button from '@mui/material/Button';
+import { Box, Snackbar, styled } from '@mui/material';
+
+const notificationsProviderSlots: NotificationsProviderSlots = {
+ snackbar: styled(Snackbar)({ position: 'absolute' }),
+};
+
+function ScopedContent() {
+ const notifications = useNotifications();
+ return (
+
+
+
+ );
+}
+
+export default function ScopedNotification() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/components/use-notifications/ScopedNotification.tsx.preview b/docs/data/toolpad/core/components/use-notifications/ScopedNotification.tsx.preview
new file mode 100644
index 00000000000..63b153cf7dd
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/ScopedNotification.tsx.preview
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/use-notifications/use-notifications-api.md b/docs/data/toolpad/core/components/use-notifications/use-notifications-api.md
new file mode 100644
index 00000000000..9c9f4ee64ad
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/use-notifications-api.md
@@ -0,0 +1,85 @@
+# useNotifications API
+
+
API reference for the useNotifications hook.
+
+:::success
+For examples and details on the usage of this React hook, visit the demo pages:
+
+- [useNotifications](/toolpad/core/react-use-notifications/)
+
+:::
+
+## Import
+
+```js
+import useNotifications from '@toolpad/core/useNotifications';
+// or
+import { useNotifications } from '@toolpad/core';
+```
+
+Learn about the difference by reading this [guide](https://mui.com/material-ui/guides/minimizing-bundle-size/) on minimizing bundle size.
+
+## Usage
+
+Get access to the notifications API by invoking the hook.
+
+```js
+const notifications = useNotifications();
+```
+
+## Reference
+
+### `notifications.show`
+
+Call the `notifications.show` method to show a snackbar in the application.
+
+```js
+const notification = notifications.show('Something great just happened!', {
+ severity: 'success',
+});
+```
+
+**Signature:**
+
+```js
+function show(message, options?)
+```
+
+**Parameters:**
+
+- `message`: `React.ReactNode` The message to show in the snackbar.
+- `options?`: `object` An options object to configure the notification. -`key?`: `string` The key to use for deduping notifications. If not provided, a unique key will be generated.
+ - `severity?`: `'info' | 'warning' | 'error' | 'success'` The severity of the notification. When provided, the snackbar will show an alert with the specified severity. Defaults to `undefined`.
+ - `autoHideDuration?`: `number` The duration in milliseconds after which the notification will automatically close. By default notifications don't hide automatically.
+ - `actionText?`: `React.ReactNode` The text to display on the action button.
+ - `onAction?`: `() => void` The callback to call when the action button is clicked.
+
+**Returns**
+
+A unique key that can be used to close the notification programmatically.
+
+### `notifications.close`
+
+programmaticaly closes a notification.
+
+```js
+const notification = notifications.show(/* ... */);
+
+// ...
+
+notifications.close(notification);
+```
+
+**Signature:**
+
+```js
+function close(notification)
+```
+
+**Parameters**
+
+- `key`: `string`
+
+**Returns**
+
+`void`
diff --git a/docs/data/toolpad/core/components/use-notifications/use-notifications.md b/docs/data/toolpad/core/components/use-notifications/use-notifications.md
new file mode 100644
index 00000000000..c500eadfec3
--- /dev/null
+++ b/docs/data/toolpad/core/components/use-notifications/use-notifications.md
@@ -0,0 +1,80 @@
+---
+productId: toolpad-core
+title: useNotifications
+components: NotificationsProvider
+---
+
+# Notifications
+
+
Imperative APIs to show and interact with application notifications
+
+Toolpad core offers a set of abstractions that make it easier to interact with notifications. Notifications are used to give short updates to the user about things that are happening during the application lifetime. They appear at the bottom of the screen. The Toolpad API allows for opening multiple notifications concurrenlty.
+
+First thing you need to do to get access to the notifications APIs is install the NotificationsProvider.
+
+```js
+import { NotificationsProvider } from '@toolpad/core/useNotifications';
+
+function App({ children }) {
+ return {children};
+}
+```
+
+Now you can get acess to the notifications APIs through the `useNotifications` hook.
+
+```js
+import { useNotifications } from '@toolpad/core/useNotifications';
+
+function MyApp() {
+ const notifications = useNotifications();
+ // ...
+}
+```
+
+:::info
+The Toolpad AppProvider automatically comes with notifications installed. You won't need to explicitly add the NotificationsProvider in Toolpad applications.
+:::
+
+## Basic notification
+
+You can notify your users with a neutral message by calling `notifications.show`. To have the notification automatically hide, add the `autoHideDuration` option. This expresses the time in milliseconds after which to close the notification.
+
+{{"demo": "BasicNotification.js"}}
+
+## Alert notifications
+
+You can send notifications under the form of alerts with the `severity` property. It takes a value from `"info"`, `"success"`, `"warning"`, or `"error"`.
+
+{{"demo": "AlertNotification.js"}}
+
+## Multiple notifications
+
+Multiple concurrent notifications are stacked and when more than one notification is available, a badge is shown with the amount of open notification. Try it out with the following demo:
+
+{{"demo": "MultipleNotifications.js"}}
+
+## Close notifications
+
+You can programmatically close existing notifications. Each notification has an associated key. You can call the `notifications.close` method with this key to close the opened notification.
+
+{{"demo": "CloseNotification.js"}}
+
+## Dedupe notifications
+
+You can supply your own value for a key to shown notifications to associate them with this key. Notifications with the same key are deduplicated as long as one is already open. If you try to show a notification with the same key, the call is simply ignored.
+
+{{"demo": "DedupeNotification.js"}}
+
+## Scoped notifications
+
+Notification providers can be nested. That way you can scope the notifications to a subset of the page. Use the slots to position the snackbar relative to a specific element on the page.
+
+{{"demo": "ScopedNotification.js"}}
+
+## 🚧 Notification center
+
+When multiple notifications are available, click the badge to open the notification center to show a scrollable list of all available notifications. This feature is not available yet.
+
+## Hook API
+
+- [`useNotifications()`](/toolpad/core/react-use-notifications/api/)
diff --git a/docs/data/toolpad/core/pages.ts b/docs/data/toolpad/core/pages.ts
index 7fae536b320..d2806c38d7a 100644
--- a/docs/data/toolpad/core/pages.ts
+++ b/docs/data/toolpad/core/pages.ts
@@ -48,7 +48,6 @@ const pages: MuiPage[] = [
},
],
},
-
{
pathname: '/toolpad/core/components-group',
title: 'Components',
@@ -88,6 +87,20 @@ const pages: MuiPage[] = [
},
],
},
+ {
+ pathname: '/toolpad/core/components/filter-group',
+ subheader: 'Utils',
+ children: [
+ {
+ pathname: '/toolpad/core/react-use-dialogs',
+ title: 'useDialogs',
+ },
+ {
+ pathname: '/toolpad/core/react-use-notifications',
+ title: 'useNotifications',
+ },
+ ],
+ },
],
},
{
diff --git a/docs/data/toolpad/core/pagesApi.js b/docs/data/toolpad/core/pagesApi.js
index b9e2c06e032..360899799b6 100644
--- a/docs/data/toolpad/core/pagesApi.js
+++ b/docs/data/toolpad/core/pagesApi.js
@@ -2,5 +2,7 @@ module.exports = [
{ pathname: '/toolpad/core/api/app-provider' },
{ pathname: '/toolpad/core/api/dashboard-layout' },
{ pathname: '/toolpad/core/api/data-grid' },
+ { pathname: '/toolpad/core/api/dialogs-provider' },
{ pathname: '/toolpad/core/api/line-chart' },
+ { pathname: '/toolpad/core/api/notifications-provider' },
];
diff --git a/docs/package.json b/docs/package.json
index ea01195f87e..d2ba150f5b4 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -33,6 +33,7 @@
"@mui/material": "next",
"@mui/material-nextjs": "next",
"@mui/styles": "next",
+ "@mui/system": "next",
"@mui/utils": "next",
"@mui/x-license": "7.7.0",
"@toolpad/core": "workspace:*",
diff --git a/docs/pages/toolpad/core/api/data-grid.json b/docs/pages/toolpad/core/api/data-grid.json
index 54797238575..724b85b8453 100644
--- a/docs/pages/toolpad/core/api/data-grid.json
+++ b/docs/pages/toolpad/core/api/data-grid.json
@@ -13,7 +13,838 @@
"import { DataGrid } from '@toolpad-core/DataGrid';",
"import { DataGrid } from '@toolpad-core';"
],
- "classes": [],
+ "classes": [
+ {
+ "key": "actionsCell",
+ "className": "",
+ "description": "Styles applied to the root element of the cell with type=\"actions\".",
+ "isGlobal": false
+ },
+ {
+ "key": "aggregationColumnHeader",
+ "className": "",
+ "description": "Styles applied to the root element of the column header when aggregated.",
+ "isGlobal": false
+ },
+ {
+ "key": "aggregationColumnHeader--alignCenter",
+ "className": "",
+ "description": "Styles applied to the root element of the header when aggregation if `headerAlign=\"center\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "aggregationColumnHeader--alignLeft",
+ "className": "",
+ "description": "Styles applied to the root element of the header when aggregation if `headerAlign=\"left\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "aggregationColumnHeader--alignRight",
+ "className": "",
+ "description": "Styles applied to the root element of the header when aggregation if `headerAlign=\"right\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "aggregationColumnHeaderLabel",
+ "className": "",
+ "description": "Styles applied to the aggregation label in the column header when aggregated.",
+ "isGlobal": false
+ },
+ {
+ "key": "autoHeight",
+ "className": "",
+ "description": "Styles applied to the root element if `autoHeight={true}`.",
+ "isGlobal": false
+ },
+ {
+ "key": "autosizing",
+ "className": "",
+ "description": "Styles applied to the root element while it is being autosized.",
+ "isGlobal": false
+ },
+ {
+ "key": "booleanCell",
+ "className": "",
+ "description": "Styles applied to the icon of the boolean cell.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell",
+ "className": "",
+ "description": "Styles applied to the cell element.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--editable",
+ "className": "",
+ "description": "Styles applied to the cell element if the cell is editable.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--editing",
+ "className": "",
+ "description": "Styles applied to the cell element if the cell is in edit mode.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--flex",
+ "className": "",
+ "description": "Styles applied to the cell element in flex display mode.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--pinnedLeft",
+ "className": "",
+ "description": "Styles applied to the cell element if it is pinned to the left.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--pinnedRight",
+ "className": "",
+ "description": "Styles applied to the cell element if it is pinned to the right.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--rangeBottom",
+ "className": "",
+ "description": "Styles applied to the cell element if it is at the bottom edge of a cell selection range.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--rangeLeft",
+ "className": "",
+ "description": "Styles applied to the cell element if it is at the left edge of a cell selection range.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--rangeRight",
+ "className": "",
+ "description": "Styles applied to the cell element if it is at the right edge of a cell selection range.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--rangeTop",
+ "className": "",
+ "description": "Styles applied to the cell element if it is at the top edge of a cell selection range.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--selectionMode",
+ "className": "",
+ "description": "Styles applied to the cell element if it is in a cell selection range.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--textCenter",
+ "className": "",
+ "description": "Styles applied to the cell element if `align=\"center\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--textLeft",
+ "className": "",
+ "description": "Styles applied to the cell element if `align=\"left\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--textRight",
+ "className": "",
+ "description": "Styles applied to the cell element if `align=\"right\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--withLeftBorder",
+ "className": "",
+ "description": "Styles applied the cell if `showColumnVerticalBorder={true}`.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--withRightBorder",
+ "className": "",
+ "description": "Styles applied the cell if `showColumnVerticalBorder={true}`.",
+ "isGlobal": false
+ },
+ {
+ "key": "cellCheckbox",
+ "className": "",
+ "description": "Styles applied to the cell checkbox element.",
+ "isGlobal": false
+ },
+ {
+ "key": "cellEmpty",
+ "className": "",
+ "description": "Styles applied to the empty cell element.",
+ "isGlobal": false
+ },
+ {
+ "key": "cellSkeleton",
+ "className": "",
+ "description": "Styles applied to the skeleton cell element.",
+ "isGlobal": false
+ },
+ {
+ "key": "checkboxInput",
+ "className": "",
+ "description": "Styles applied to the selection checkbox element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader",
+ "className": "",
+ "description": "Styles applied to the column header element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--alignCenter",
+ "className": "",
+ "description": "Styles applied to the column header if `headerAlign=\"center\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--alignLeft",
+ "className": "",
+ "description": "Styles applied to the column header if `headerAlign=\"left\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--alignRight",
+ "className": "",
+ "description": "Styles applied to the column header if `headerAlign=\"right\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--dragging",
+ "className": "",
+ "description": "Styles applied to the floating column header element when it is dragged.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--emptyGroup",
+ "className": "",
+ "description": "Styles applied to the empty column group header cell.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--filledGroup",
+ "className": "",
+ "description": "Styles applied to the column group header cell if not empty.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--filtered",
+ "className": "",
+ "description": "Styles applied to the column header if the column has a filter applied to it.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--last",
+ "className": "",
+ "description": "Styles applied to the last column header element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--moving",
+ "className": "",
+ "description": "Styles applied to the column header if it is being dragged.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--numeric",
+ "className": "",
+ "description": "Styles applied to the column header if the type of the column is `number`.",
+ "isGlobal": false
+ },
+ { "key": "columnHeader--pinnedLeft", "className": "", "description": "", "isGlobal": false },
+ { "key": "columnHeader--pinnedRight", "className": "", "description": "", "isGlobal": false },
+ {
+ "key": "columnHeader--sortable",
+ "className": "",
+ "description": "Styles applied to the column header if the column is sortable.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--sorted",
+ "className": "",
+ "description": "Styles applied to the column header if the column is sorted.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--withLeftBorder",
+ "className": "",
+ "description": "",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--withRightBorder",
+ "className": "",
+ "description": "Styles applied the column header if `showColumnVerticalBorder={true}`.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeaderCheckbox",
+ "className": "",
+ "description": "Styles applied to the header checkbox cell element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeaderDraggableContainer",
+ "className": "",
+ "description": "Styles applied to the column header's draggable container element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeaders",
+ "className": "",
+ "description": "Styles applied to the column headers.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeaderTitle",
+ "className": "",
+ "description": "Styles applied to the column header's title element;",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeaderTitleContainer",
+ "className": "",
+ "description": "Styles applied to the column header's title container element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeaderTitleContainerContent",
+ "className": "",
+ "description": "Styles applied to the column header's title excepted buttons.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnSeparator",
+ "className": "",
+ "description": "Styles applied to the column header separator element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnSeparator--resizable",
+ "className": "",
+ "description": "Styles applied to the column header separator if the column is resizable.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnSeparator--resizing",
+ "className": "",
+ "description": "Styles applied to the column header separator if the column is being resized.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnSeparator--sideLeft",
+ "className": "",
+ "description": "Styles applied to the column header separator if the side is \"left\".",
+ "isGlobal": false
+ },
+ {
+ "key": "columnSeparator--sideRight",
+ "className": "",
+ "description": "Styles applied to the column header separator if the side is \"right\".",
+ "isGlobal": false
+ },
+ {
+ "key": "columnsManagement",
+ "className": "",
+ "description": "Styles applied to the columns management body.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnsManagementFooter",
+ "className": "",
+ "description": "Styles applied to the columns management footer element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnsManagementHeader",
+ "className": "",
+ "description": "Styles applied to the columns management header element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnsManagementRow",
+ "className": "",
+ "description": "Styles applied to the columns management row element.",
+ "isGlobal": false
+ },
+ {
+ "key": "container--bottom",
+ "className": "",
+ "description": "Styles applied to the bottom container.",
+ "isGlobal": false
+ },
+ {
+ "key": "container--top",
+ "className": "",
+ "description": "Styles applied to the top container.",
+ "isGlobal": false
+ },
+ {
+ "key": "detailPanel",
+ "className": "",
+ "description": "Styles applied to the detail panel element.",
+ "isGlobal": false
+ },
+ {
+ "key": "detailPanels",
+ "className": "",
+ "description": "Styles applied to the detail panels wrapper element.",
+ "isGlobal": false
+ },
+ {
+ "key": "detailPanelToggleCell",
+ "className": "",
+ "description": "Styles applied to the detail panel toggle cell element.",
+ "isGlobal": false
+ },
+ {
+ "key": "detailPanelToggleCell--expanded",
+ "className": "",
+ "description": "Styles applied to the detail panel toggle cell element if expanded.",
+ "isGlobal": false
+ },
+ {
+ "key": "editBooleanCell",
+ "className": "",
+ "description": "Styles applied to root of the boolean edit component.",
+ "isGlobal": false
+ },
+ {
+ "key": "editInputCell",
+ "className": "",
+ "description": "Styles applied to the root of the input component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterForm",
+ "className": "",
+ "description": "Styles applied to the root of the filter form component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterFormColumnInput",
+ "className": "",
+ "description": "Styles applied to the column input of the filter form component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterFormDeleteIcon",
+ "className": "",
+ "description": "Styles applied to the delete icon of the filter form component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterFormLogicOperatorInput",
+ "className": "",
+ "description": "Styles applied to the link operator input of the filter form component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterFormOperatorInput",
+ "className": "",
+ "description": "Styles applied to the operator input of the filter form component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterFormValueInput",
+ "className": "",
+ "description": "Styles applied to the value input of the filter form component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterIcon",
+ "className": "",
+ "description": "Styles applied to the filter icon element.",
+ "isGlobal": false
+ },
+ {
+ "key": "footerCell",
+ "className": "",
+ "description": "Styles applied to the root element of the cell inside a footer row.",
+ "isGlobal": false
+ },
+ {
+ "key": "footerContainer",
+ "className": "",
+ "description": "Styles applied to the footer container element.",
+ "isGlobal": false
+ },
+ {
+ "key": "groupingCriteriaCell",
+ "className": "",
+ "description": "Styles applied to the root element of the grouping criteria cell",
+ "isGlobal": false
+ },
+ {
+ "key": "groupingCriteriaCellToggle",
+ "className": "",
+ "description": "Styles applied to the toggle of the grouping criteria cell",
+ "isGlobal": false
+ },
+ {
+ "key": "headerFilterRow",
+ "className": "",
+ "description": "Styles applied to the column header filter row.",
+ "isGlobal": false
+ },
+ {
+ "key": "iconButtonContainer",
+ "className": "",
+ "description": "Styles applied to the column header icon's container.",
+ "isGlobal": false
+ },
+ {
+ "key": "iconSeparator",
+ "className": "",
+ "description": "Styles applied to the column header separator icon element.",
+ "isGlobal": false
+ },
+ {
+ "key": "main",
+ "className": "",
+ "description": "Styles applied to the main container element.",
+ "isGlobal": false
+ },
+ {
+ "key": "main--hasPinnedRight",
+ "className": "",
+ "description": "Styles applied to the main container element when it has right pinned columns.",
+ "isGlobal": false
+ },
+ {
+ "key": "menu",
+ "className": "",
+ "description": "Styles applied to the menu element.",
+ "isGlobal": false
+ },
+ {
+ "key": "menuIcon",
+ "className": "",
+ "description": "Styles applied to the menu icon element.",
+ "isGlobal": false
+ },
+ {
+ "key": "menuIconButton",
+ "className": "",
+ "description": "Styles applied to the menu icon button element.",
+ "isGlobal": false
+ },
+ {
+ "key": "menuList",
+ "className": "",
+ "description": "Styles applied to the menu list element.",
+ "isGlobal": false
+ },
+ {
+ "key": "menuOpen",
+ "className": "",
+ "description": "Styles applied to the menu icon element if the menu is open.",
+ "isGlobal": false
+ },
+ {
+ "key": "overlay",
+ "className": "",
+ "description": "Styles applied to the overlay element.",
+ "isGlobal": false
+ },
+ {
+ "key": "overlayWrapper",
+ "className": "",
+ "description": "Styles applied to the overlay wrapper element.",
+ "isGlobal": false
+ },
+ {
+ "key": "overlayWrapperInner",
+ "className": "",
+ "description": "Styles applied to the overlay wrapper inner element.",
+ "isGlobal": false
+ },
+ {
+ "key": "panel",
+ "className": "",
+ "description": "Styles applied to the panel element.",
+ "isGlobal": false
+ },
+ {
+ "key": "panelContent",
+ "className": "",
+ "description": "Styles applied to the panel content element.",
+ "isGlobal": false
+ },
+ {
+ "key": "panelFooter",
+ "className": "",
+ "description": "Styles applied to the panel footer element.",
+ "isGlobal": false
+ },
+ {
+ "key": "panelHeader",
+ "className": "",
+ "description": "Styles applied to the panel header element.",
+ "isGlobal": false
+ },
+ {
+ "key": "panelWrapper",
+ "className": "",
+ "description": "Styles applied to the panel wrapper element.",
+ "isGlobal": false
+ },
+ {
+ "key": "paper",
+ "className": "",
+ "description": "Styles applied to the paper element.",
+ "isGlobal": false
+ },
+ {
+ "key": "pinnedColumns",
+ "className": "",
+ "description": "Styles applied to the pinned columns.",
+ "isGlobal": false
+ },
+ {
+ "key": "pinnedRows",
+ "className": "",
+ "description": "Styles applied to the pinned rows container.",
+ "isGlobal": false
+ },
+ {
+ "key": "pinnedRows--bottom",
+ "className": "",
+ "description": "Styles applied to the bottom pinned rows container.",
+ "isGlobal": false
+ },
+ {
+ "key": "pinnedRows--top",
+ "className": "",
+ "description": "Styles applied to the top pinned rows container.",
+ "isGlobal": false
+ },
+ {
+ "key": "pinnedRowsRenderZone",
+ "className": "",
+ "description": "Styles applied to pinned rows render zones.",
+ "isGlobal": false
+ },
+ {
+ "key": "root",
+ "className": "",
+ "description": "Styles applied to the root element.",
+ "isGlobal": false
+ },
+ {
+ "key": "root--densityComfortable",
+ "className": "",
+ "description": "Styles applied to the root element if density is \"comfortable\".",
+ "isGlobal": false
+ },
+ {
+ "key": "root--densityCompact",
+ "className": "",
+ "description": "Styles applied to the root element if density is \"compact\".",
+ "isGlobal": false
+ },
+ {
+ "key": "root--densityStandard",
+ "className": "",
+ "description": "Styles applied to the root element if density is \"standard\" (default).",
+ "isGlobal": false
+ },
+ {
+ "key": "root--disableUserSelection",
+ "className": "",
+ "description": "Styles applied to the root element when user selection is disabled.",
+ "isGlobal": false
+ },
+ {
+ "key": "row",
+ "className": "",
+ "description": "Styles applied to the row element.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--detailPanelExpanded",
+ "className": "",
+ "description": "Styles applied to the row if its detail panel is open.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--dragging",
+ "className": "",
+ "description": "Styles applied to the floating special row reorder cell element when it is dragged.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--dynamicHeight",
+ "className": "",
+ "description": "Styles applied to the row if it has dynamic row height.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--editable",
+ "className": "",
+ "description": "Styles applied to the row element if the row is editable.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--editing",
+ "className": "",
+ "description": "Styles applied to the row element if the row is in edit mode.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--firstVisible",
+ "className": "",
+ "description": "Styles applied to the first visible row element on every page of the grid.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--lastVisible",
+ "className": "",
+ "description": "Styles applied to the last visible row element on every page of the grid.",
+ "isGlobal": false
+ },
+ {
+ "key": "rowCount",
+ "className": "",
+ "description": "Styles applied to the footer row count element to show the total number of rows.\nOnly works when pagination is disabled.",
+ "isGlobal": false
+ },
+ {
+ "key": "rowReorderCell",
+ "className": "",
+ "description": "Styles applied to the root element of the row reorder cell",
+ "isGlobal": false
+ },
+ {
+ "key": "rowReorderCell--draggable",
+ "className": "",
+ "description": "Styles applied to the root element of the row reorder cell when dragging is allowed",
+ "isGlobal": false
+ },
+ {
+ "key": "rowReorderCellContainer",
+ "className": "",
+ "description": "Styles applied to the row reorder cell container element.",
+ "isGlobal": false
+ },
+ {
+ "key": "rowReorderCellPlaceholder",
+ "className": "",
+ "description": "Styles applied to the row's draggable placeholder element inside the special row reorder cell.",
+ "isGlobal": false
+ },
+ {
+ "key": "scrollArea",
+ "className": "",
+ "description": "Styles applied to both scroll area elements.",
+ "isGlobal": false
+ },
+ {
+ "key": "scrollArea--left",
+ "className": "",
+ "description": "Styles applied to the left scroll area element.",
+ "isGlobal": false
+ },
+ {
+ "key": "scrollArea--right",
+ "className": "",
+ "description": "Styles applied to the right scroll area element.",
+ "isGlobal": false
+ },
+ {
+ "key": "scrollbar",
+ "className": "",
+ "description": "Styles applied to the scrollbars.",
+ "isGlobal": false
+ },
+ {
+ "key": "scrollbar--horizontal",
+ "className": "",
+ "description": "Styles applied to the horizontal scrollbar.",
+ "isGlobal": false
+ },
+ {
+ "key": "scrollbar--vertical",
+ "className": "",
+ "description": "Styles applied to the horizontal scrollbar.",
+ "isGlobal": false
+ },
+ {
+ "key": "selectedRowCount",
+ "className": "",
+ "description": "Styles applied to the footer selected row count element.",
+ "isGlobal": false
+ },
+ {
+ "key": "sortIcon",
+ "className": "",
+ "description": "Styles applied to the sort icon element.",
+ "isGlobal": false
+ },
+ {
+ "key": "toolbarContainer",
+ "className": "",
+ "description": "Styles applied to the toolbar container element.",
+ "isGlobal": false
+ },
+ {
+ "key": "toolbarFilterList",
+ "className": "",
+ "description": "Styles applied to the toolbar filter list element.",
+ "isGlobal": false
+ },
+ {
+ "key": "treeDataGroupingCell",
+ "className": "",
+ "description": "Styles applied to the root of the grouping column of the tree data.",
+ "isGlobal": false
+ },
+ {
+ "key": "treeDataGroupingCellToggle",
+ "className": "",
+ "description": "Styles applied to the toggle of the grouping cell of the tree data.",
+ "isGlobal": false
+ },
+ {
+ "key": "virtualScroller",
+ "className": "",
+ "description": "Styles applied to the virtualization container.",
+ "isGlobal": false
+ },
+ {
+ "key": "virtualScrollerContent",
+ "className": "",
+ "description": "Styles applied to the virtualization content.",
+ "isGlobal": false
+ },
+ {
+ "key": "virtualScrollerContent--overflowed",
+ "className": "",
+ "description": "Styles applied to the virtualization content when its height is bigger than the virtualization container.",
+ "isGlobal": false
+ },
+ {
+ "key": "virtualScrollerRenderZone",
+ "className": "",
+ "description": "Styles applied to the virtualization render zone.",
+ "isGlobal": false
+ },
+ {
+ "key": "withBorderColor",
+ "className": "",
+ "description": "Styles applied to cells, column header and other elements that have border.\nSets border color only.",
+ "isGlobal": false
+ },
+ {
+ "key": "withVerticalBorder",
+ "className": "",
+ "description": "Styles applied the grid if `showColumnVerticalBorder={true}`.",
+ "isGlobal": false
+ }
+ ],
"spread": true,
"themeDefaultProps": false,
"muiName": "DataGrid",
diff --git a/docs/pages/toolpad/core/api/dialogs-provider.js b/docs/pages/toolpad/core/api/dialogs-provider.js
new file mode 100644
index 00000000000..5f44132665d
--- /dev/null
+++ b/docs/pages/toolpad/core/api/dialogs-provider.js
@@ -0,0 +1,23 @@
+import * as React from 'react';
+import ApiPage from 'docs/src/modules/components/ApiPage';
+import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations';
+import jsonPageContent from './dialogs-provider.json';
+
+export default function Page(props) {
+ const { descriptions, pageContent } = props;
+ return ;
+}
+
+Page.getInitialProps = () => {
+ const req = require.context(
+ 'docs-toolpad/translations/api-docs/dialogs-provider',
+ false,
+ /\.\/dialogs-provider.*.json$/,
+ );
+ const descriptions = mapApiPageTranslations(req);
+
+ return {
+ descriptions,
+ pageContent: jsonPageContent,
+ };
+};
diff --git a/docs/pages/toolpad/core/api/dialogs-provider.json b/docs/pages/toolpad/core/api/dialogs-provider.json
new file mode 100644
index 00000000000..2310fc48fe5
--- /dev/null
+++ b/docs/pages/toolpad/core/api/dialogs-provider.json
@@ -0,0 +1,13 @@
+{
+ "props": {},
+ "name": "DialogsProvider",
+ "imports": ["import { DialogsProvider } from '@toolpad-core/useDialogs';"],
+ "classes": [],
+ "spread": true,
+ "themeDefaultProps": false,
+ "muiName": "DialogsProvider",
+ "filename": "/packages/toolpad-core/src/useDialogs/DialogsProvider.tsx",
+ "inheritance": null,
+ "demos": "
+ ) : null;
+ }
+ const { result, rerender } = renderHook(() => useDialogs(), { wrapper: TestWrapper });
+
+ const dialogResult = result.current.open(CustomDialog);
+
+ const dialog = await screen.findByRole('dialog');
+
+ rerender();
+
+ await userEvent.click(within(dialog).getByRole('button', { name: 'Close me' }));
+
+ rerender();
+
+ expect(await dialogResult).toBe('I am result');
+ });
+ });
+});
diff --git a/packages/toolpad-core/src/useDialogs/useDialogs.tsx b/packages/toolpad-core/src/useDialogs/useDialogs.tsx
new file mode 100644
index 00000000000..c73fe77ff58
--- /dev/null
+++ b/packages/toolpad-core/src/useDialogs/useDialogs.tsx
@@ -0,0 +1,328 @@
+import { LoadingButton } from '@mui/lab';
+import {
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ TextField,
+ DialogContentText,
+} from '@mui/material';
+import { useNonNullableContext } from '@toolpad/utils/react';
+import invariant from 'invariant';
+import * as React from 'react';
+import { DialogsContext } from './DialogsContext';
+
+export interface OpenDialogOptions {
+ /**
+ * A function that is called before closing the dialog closes. The dialog
+ * stays open as long as the returned promise is not resolved. Use this if
+ * you want to perform an async action on close and show a loading state.
+ *
+ * @param result The result that the dialog will return after closing.
+ * @returns A promise that resolves when the dialog can be closed.
+ */
+ onClose?: (result: R) => Promise;
+}
+
+export interface AlertOptions extends OpenDialogOptions {
+ /**
+ * A title for the dialog. Defaults to `'Alert'`.
+ */
+ title?: React.ReactNode;
+ /**
+ * The text to show in the "Ok" button. Defaults to `'Ok'`.
+ */
+ okText?: React.ReactNode;
+}
+
+export interface ConfirmOptions extends OpenDialogOptions {
+ /**
+ * A title for the dialog. Defaults to `'Confirm'`.
+ */
+ title?: React.ReactNode;
+ /**
+ * The text to show in the "Ok" button. Defaults to `'Ok'`.
+ */
+ okText?: React.ReactNode;
+ /**
+ * Denotes the purpose of the dialog. This will affect the color of the
+ * "Ok" button. Defaults to `undefined`.
+ */
+ severity?: 'error' | 'info' | 'success' | 'warning';
+ /**
+ * The text to show in the "Cancel" button. Defaults to `'Cancel'`.
+ */
+ cancelText?: React.ReactNode;
+}
+
+export interface PromptOptions extends OpenDialogOptions {
+ /**
+ * A title for the dialog. Defaults to `'Prompt'`.
+ */
+ title?: React.ReactNode;
+ /**
+ * The text to show in the "Ok" button. Defaults to `'Ok'`.
+ */
+ okText?: React.ReactNode;
+ /**
+ * The text to show in the "Cancel" button. Defaults to `'Cancel'`.
+ */
+ cancelText?: React.ReactNode;
+}
+
+/**
+ * The props that are passed to a dialog component.
+ */
+export interface DialogProps
{
+ /**
+ * The payload that was passed when the dialog was opened.
+ */
+ payload: P;
+ /**
+ * Whether the dialog is open.
+ */
+ open: boolean;
+ /**
+ * A function to call when the dialog should be closed. If the dialog has a return
+ * value, it should be passed as an argument to this function. You should use the promise
+ * that is returned to show a loading state while the dialog is performing async actions
+ * on close.
+ * @param result The result to return from the dialog.
+ * @returns A promise that resolves when the dialog can be fully closed.
+ */
+ onClose: (result: R) => Promise;
+}
+
+export interface OpenAlertDialog {
+ /**
+ * Open an alert dialog. Returns a promise that resolves when the user
+ * closes the dialog.
+ *
+ * @param msg The message to show in the dialog.
+ * @param options Additional options for the dialog.
+ * @returns A promise that resolves when the dialog is closed.
+ */
+ (msg: React.ReactNode, options?: AlertOptions): Promise;
+}
+
+export interface OpenConfirmDialog {
+ /**
+ * Open a confirmation dialog. Returns a promise that resolves to true if
+ * the user confirms, false if the user cancels.
+ *
+ * @param msg The message to show in the dialog.
+ * @param options Additional options for the dialog.
+ * @returns A promise that resolves to true if the user confirms, false if the user cancels.
+ */
+ (msg: React.ReactNode, options?: ConfirmOptions): Promise;
+}
+
+export interface OpenPromptDialog {
+ /**
+ * Open a prompt dialog to request user input. Returns a promise that resolves to the input
+ * if the user confirms, null if the user cancels.
+ *
+ * @param msg The message to show in the dialog.
+ * @param options Additional options for the dialog.
+ * @returns A promise that resolves to the user input if the user confirms, null if the user cancels.
+ */
+ (msg: React.ReactNode, options?: PromptOptions): Promise;
+}
+
+export type DialogComponent
= React.ComponentType>;
+
+export interface OpenDialog {
+ /**
+ * Open a dialog without payload.
+ * @param Component The dialog component to open.
+ * @param options Additional options for the dialog.
+ */
+
(
+ Component: DialogComponent
,
+ payload?: P,
+ options?: OpenDialogOptions,
+ ): Promise;
+ /**
+ * Open a dialog and pass a payload.
+ * @param Component The dialog component to open.
+ * @param payload The payload to pass to the dialog.
+ * @param options Additional options for the dialog.
+ */
+