Windows
Overview
⚠️ Note that this feature is still experimental in io.Connect Desktop.
io.Connect Desktop supports a set of predefined modal windows (alerts and dialogs), which you can use in your interop-enabled apps via the @interopio/modals-api library. The Modals API also enables you to display your own custom alerts and dialogs.
⚠️ Note that you can also use the
showDialog()method of anIOConnectWindowinstance to show dialogs from your apps. This method is still supported and you can use it to show the already existing set of predefined dialogs, as well as custom ones, but it doesn't support the set of predefined dialogs available via the new@interopio/modals-apilibrary. Respectively, the@interopio/modals-apilibrary doesn't support the predefined dialogs available via theshowDialog()method.
Configuration
The following sections describe the available configuration settings for the io.Connect alerts and dialogs.
Alerts
Available since io.Connect Desktop 9.9
To provide settings for alert windows, use the "alerts" top-level key in the system.json system configuration file of io.Connect Desktop:
{
"alerts": {
"enabled": true,
"width": 450,
"height": 100,
"ttl": 7000,
"roundedCorners": true
}
}The "alerts" object has the following properties:
| Property | Type | Description |
|---|---|---|
"enabled" |
boolean |
If true (default), will enable the io.Connect alerts. |
"height" |
number |
Height in pixels for the io.Connect alerts. Defaults to 82. |
"roundedCorners" |
boolean |
If true, the io.Connect Window containing the alerts will have rounded corners. Defaults to false. |
"transparent" |
boolean |
If true (default), the io.Connect Window containing the alerts will be transparent. |
"ttl" |
number |
Interval in milliseconds that defines how long the alert is displayed to the user before it automatically disappears. Defaults to 5000. |
"url" |
string |
URL pointing to the location of the io.Connect alerts app. You can use this to provide a URL pointing to your custom alerts app. |
"width" |
number |
Width in pixels for the io.Connect alerts. Defaults to 400. |
Dialogs
To provide settings for dialog windows, use the "dialogs" top-level key in the system.json system configuration file of io.Connect Desktop:
{
"dialogs": {
"url": "https://my-custom-dialog.com",
"width": 380,
"height": 150,
"enableDefaultDialogs": {
"preventShutdown": false,
"preventWindowClose": false
}
}
}The "dialogs" object has the following properties:
| Property | Type | Description |
|---|---|---|
"enableDefaultDialogs" |
object |
Enable or disable the default io.Connect dialog messages. |
"height" |
number |
Height in pixels for the io.Connect dialogs. Defaults to 205. |
"transparent" |
boolean |
If true, the io.Connect Window containing the dialog will be transparent. |
"url" |
string |
URL pointing to the location of the io.Connect dialog app. |
"width" |
number |
Width in pixels for the io.Connect dialogs. Defaults to 400. |
The "enableDefaultDialogs" object has the following properties:
| Property | Type | Description |
|---|---|---|
"preventShutdown" |
boolean |
If true (default), will enable the dialog message that appears when io.Connect Desktop is about to shutdown. |
"preventWindowClose" |
boolean |
If true (default), will enable the dialog message that appears when the user tries to close an io.Connect Window that is programmatically prevented from closing. This dialog will appear also when such window participates in a Layout that is being closed. |
"unsavedLayoutChanges" |
boolean |
If true, will enable the dialog message that appears when the user modifies the current Global Layout and tries to restore another Global Layout. Defaults to false. Available since io.Connect Desktop 9.2. |
Available since io.Connect Desktop 10.5
In the default platform mode, dialogs are modal on all supported operating systems. A dialog for a specific window blocks the window group or the Workspaces App instance containing the window, and a global dialog (e.g., for shutting down the platform or for unsaved Layout changes) blocks all io.Connect Windows, including the ones opened while the dialog is displayed. The blocked windows are covered with an overlay and don't receive user input until the dialog is closed.
Custom Alerts
Besides using the available io.Connect alerts, you can also define an entirely custom alert app to be used by the platform.
To create a custom alert app:
- Implement the app with the alert service that io.Connect Desktop injects in the alert app, or with the components provided by the
@interopio/components-reactlibrary. - Configure the app as the alert app of io.Connect Desktop.
- Customize the alert styles.
Alert Service
Available since io.Connect Desktop 10.2
The custom alert app can use the ioAlert service object injected in the global window object to receive the alert configuration sent by the client app and to communicate back to the platform.
The ioAlert object has the following methods:
| Method | Description |
|---|---|
getConfig() |
Resolves with the AlertRequestConfig object passed to the request() method by the client app. Use this method to receive the alert options provided by the app that triggered the alert. |
getTheme() |
Returns the name of the current io.Connect theme as a string. Use this to apply the appropriate theme styling to the custom alert. |
onThemeChanged() |
Notifies when the theme changes. Receives a callback function as an argument. The callback will be invoked with the name of the new theme as an argument. |
setResult() |
Sends the result of the user interaction back to the client app and closes the alert. Receives the data to return as an argument, which can be any type. |
The following example demonstrates how to use the ioAlert object in a custom alert app to retrieve the alert options provided by the client app and handle user interactions:
// Retrieve the alert options provided by the client app.
const {
variant,
text,
actions,
showCloseButton
} = await ioAlert.getConfig();
// Apply the alert options to the alert UI.
document.getElementById("alert-text").textContent = text;
document.getElementById("alert-container").classList.add(variant);
// Apply the current io.Connect theme.
const ioTheme = ioAlert.getTheme();
document.documentElement.classList.add(ioTheme);
// React to theme changes.
ioAlert.onThemeChanged((newTheme) => {
document.documentElement.classList.replace(ioTheme, newTheme);
});
// Handle action button clicks.
if (actions) {
actions.forEach((action) => {
const button = document.createElement("button");
button.textContent = action.title;
button.addEventListener("click", () => {
// Return the result to the client app and close the alert.
ioAlert.setResult({ action: action.title });
});
document.getElementById("actions-container").appendChild(button);
});
}
// Handle close button click.
if (showCloseButton) {
document.getElementById("close-button").addEventListener("click", () => {
ioAlert.setResult({ action: "dismissed" });
});
}Components
Available since io.Connect Desktop 10.2 & @interopio/components-react 4.3
The default alert app is built with the components provided by the IOAlerts and IOAlertTemplates objects of the @interopio/components-react library. You can use the same components to create a custom alert app.
The <AlertsProvider /> component of IOAlerts supplies the current theme to the alert app. The <Alerts /> component of IOAlertTemplates receives the alerts from the platform and renders them. You must render the <Alerts /> component within the <AlertsProvider /> component. The alert app doesn't need to be rendered within the <IOConnectProvider /> component.
The <Alerts /> component has the following props:
| Prop | Type | Description |
|---|---|---|
Alert |
React.ComponentType |
Component for rendering the alert. Defaults to the <DefaultAlert /> component of IOAlertTemplates. |
messagePort |
object |
Required. Object that delivers the alerts from the platform and sends the user interactions back to it. Pass the ioAlert object injected in the global window object. |
The component passed in the Alert prop receives the following props:
| Prop | Type | Description |
|---|---|---|
data |
object |
The alert to render. Has an id property and a config property that holds the AlertRequestConfig object passed by the client app, without its target and ttl properties. |
onClick |
function |
Callback for reporting a user interaction to the platform. Accepts an object with optional interopAction and shouldCloseAlert properties. The interopAction object has a name property and a settings property that holds the Interop method to invoke as described in the alert configuration. If shouldCloseAlert is true, the platform will close the alert. |
The <Actions /> component of IOAlertTemplates renders a button for each alert action. It has an actions prop that accepts the alert actions and an onActionClick prop that accepts a callback that will be invoked with the click event and the clicked action.
The following example demonstrates creating an alert app that renders a custom alert with the action buttons of the default alert:
import { useEffect } from "react";
import { IOAlerts, IOAlertTemplates } from "@interopio/components-react";
import "@interopio/theme";
import "@interopio/components-react/dist/styles/features/alerts/styles.css";
const { AlertsProvider, useAlertsContext } = IOAlerts;
const { Alerts, Actions } = IOAlertTemplates;
const MyAlert = ({ data, onClick }) => {
const { text, actions, clickInterop } = data.config;
const handleClick = () => {
const interopAction = clickInterop ? { name: "click", settings: clickInterop } : undefined;
onClick({ interopAction, shouldCloseAlert: true });
};
const handleActionClick = (event, action) => {
event.stopPropagation();
onClick({ interopAction: { name: action.title, settings: action.clickInterop }, shouldCloseAlert: true });
};
return (
<div className="my-alert" onClick={handleClick}>
<p>{text}</p>
{actions?.length > 0 && <Actions actions={actions} onActionClick={handleActionClick} />}
</div>
);
};
const AlertApp = () => {
const { theme } = useAlertsContext();
// Apply the current theme to the alert app.
useEffect(() => {
document.documentElement.classList.add(theme);
return () => document.documentElement.classList.remove(theme);
}, [theme]);
return <Alerts messagePort={window.ioAlert} Alert={MyAlert} />;
};
const App = () => {
return (
<AlertsProvider>
<AlertApp />
</AlertsProvider>
);
};
export default App;To render the default alerts, use the <Alert /> component of IOAlerts within the <AlertsProvider /> component. The <Alert /> component applies the current theme and renders the <Alerts /> component with the default alert.
Configuration
To use a custom alert app, set the "url" property of the "alerts" top-level key in the system.json system configuration file of io.Connect Desktop to the location of your custom alert app:
{
"alerts": {
"url": "https://my-custom-alert.com"
}
}When an interop-enabled app invokes the request() method of the Alerts API, io.Connect Desktop will load the custom alert app instead of the default one.
Custom Dialogs
⚠️ Note that the described functionalities are available when using the
request()of the Dialogs API, as well as theshowDialog()method of anIOConnectWindowor aFrameinstance. However, it's highly recommended to use the Dialogs API to display dialogs instead of theshowDialog()method.
Besides using the available io.Connect dialogs, you can also define an entirely custom dialog app to be used by the platform.
To create a custom dialog app:
- Implement the app with the dialog service that io.Connect Desktop injects in the dialog app, or with the components provided by the
@interopio/components-reactlibrary. - Configure the app as the dialog app of io.Connect Desktop.
- Customize the dialog styles.
Dialog Service
Available since io.Connect Desktop 10.2
The custom dialog app can use the ioDialog service object injected in the global window object to receive the dialog options sent by the client app and to return data back to it.
The ioDialog object has the following methods:
| Method | Description |
|---|---|
getConfig() |
Resolves with the dialog options passed by the client app. If the dialog was triggered via the request() method of the Dialogs API, the returned value will be a DialogRequestConfig object. If the dialog was triggered via the showDialog() method of an IOConnectWindow or a Workspace Frame instance, the returned value will be a DialogOptions object. Use this method to receive the dialog options and determine how to render the dialog. |
getTheme() |
Returns the name of the current io.Connect theme as a string. Use this to apply the appropriate theme styling to the custom dialog. |
onThemeChanged() |
Notifies when the theme changes. Receives a callback function as an argument. The callback will be invoked with the name of the new theme as an argument. |
setResult() |
Sends the result of the user interaction back to the client app and closes the dialog. Receives the data to return as an argument, which can be any type. |
The following example demonstrates how to use the ioDialog object in a custom dialog app to retrieve the dialog options provided by the client app and return data back to it:
// Retrieve the dialog options provided by the client app.
const { templateName, variables } = await ioDialog.getConfig();
// Apply the dialog options to the dialog UI.
document.getElementById("dialog-title").textContent = variables.title;
document.getElementById("dialog-message").textContent = variables.text;
// Apply the current io.Connect theme.
const ioTheme = ioDialog.getTheme();
document.documentElement.classList.add(ioTheme);
// React to theme changes.
const themeHandler = newTheme => document.documentElement.classList.replace(ioTheme, newTheme);
ioDialog.onThemeChanged(themeHandler);
// React to user interaction and return the result to the client app.
// The returned data can be of any type.
const result = { buttonClicked: null, context: 42 };
// Determine the template of the requested dialog and react accordingly.
if (templateName === "MyCustomDialog") {
result.buttonClicked = { id: "confirm", text: "Confirm" };
}
const clickHandler = () => ioDialog.setResult(result);
document.getElementById("confirm-button").addEventListener("click", clickHandler);You can specify a custom templateName in your client app and invoke the request() method with it:
const myDialog = {
// Specifying a custom dialog template.
templateName: "MyCustomDialog",
variables: {
title: "Custom Title",
text: "Custom Message"
}
};
// The client app will receive the result from opening the custom dialog
// in the exact shape as returned by the custom dialog app.
const { buttonClicked, context } = await io.modals.dialogs.request(myDialog);
// Handle the dialog result with your custom logic.
handleDialogResult(buttonClicked, context);Components
The default dialog app is built with the components provided by the IODialogs and IODialogTemplates objects of the @interopio/components-react library. You can use the same components to create a custom dialog app.
The dialog app shows two kinds of dialogs:
- Platform dialogs that io.Connect Desktop shows on platform events, or that apps show via the
showDialog()method. Theoperationproperty of their configuration is"systemShutdown","systemRestart","windowClose", or"layoutRestore". - Template dialogs that apps request via the
request()method of the Dialogs API. Theoperationproperty of their configuration is"requestDialog".
The <DialogsProvider /> component of IODialogs receives the dialog configuration from the platform and applies the current theme. You must render the dialogs within the <DialogsProvider /> component. The dialog app doesn't need to be rendered within the <IOConnectProvider /> component.
You must also import the CSS files for the dialogs and for the dialog templates.
The following example renders the default dialogs:
import { IODialogs, IODialogTemplates } from "@interopio/components-react";
import "@interopio/theme";
import "@interopio/components-react/dist/styles/features/dialogs/styles.css";
import "@interopio/components-react/dist/styles/features/dialog-templates/styles.css";
const {
DialogsProvider,
useDialogsContext,
Dialog,
SingleInputDialog
} = IODialogs;
const { Dialogs, DEFAULT_DIALOG_TEMPLATES } = IODialogTemplates;
const DialogApp = () => {
const { config, setResult } = useDialogsContext();
if (config.operation === "requestDialog") {
return <Dialogs messagePort={window.ioDialog} templates={DEFAULT_DIALOG_TEMPLATES} />;
}
if (config.type === "SingleInputDialog") {
return <SingleInputDialog config={config} setResult={setResult} />;
}
return <Dialog config={config} setResult={setResult} />;
};
const App = () => {
return (
<DialogsProvider>
<DialogApp />
</DialogsProvider>
);
};
export default App;The useDialogsContext() hook returns an object with the following properties:
| Property | Type | Description |
|---|---|---|
config |
object |
Configuration of the dialog to show. |
setResult |
function |
Accepts an object describing the user interaction. Sends the result to the platform and closes the dialog. |
theme |
string |
Name of the current theme. |
Platform Dialogs
The <Dialog /> and <SingleInputDialog /> components of IODialogs render a platform dialog based on its configuration. They have the following props:
| Prop | Type | Description |
|---|---|---|
body |
React.ReactNode |
Content to render instead of the default body of the dialog. |
config |
object |
Required. Configuration of the dialog. Pass the config property returned by the useDialogsContext() hook. |
footer |
React.ReactNode |
Content to render instead of the default footer of the dialog. |
header |
React.ReactNode |
Content to render instead of the default header of the dialog. |
setResult |
function |
Required. Callback for sending the result to the platform. Pass the setResult property returned by the useDialogsContext() hook. |
The dialog configuration has, among others, the following properties:
| Property | Type | Description |
|---|---|---|
context |
object |
Context data for the dialog. For the "systemShutdown", "systemRestart", and "layoutRestore" operations, has an isLayoutModified property that is true if the current Global Layout has unsaved changes. |
message |
string |
Message to show in the dialog. |
messageTitle |
string |
Title of the message. |
operation |
string |
Operation that has triggered the dialog. |
title |
string |
Title of the dialog. |
type |
string |
Type of the dialog - e.g., "YesNo", "OkCancel", "SingleInputDialog". |
The object passed to the setResult() method has the following properties:
| Property | Type | Description |
|---|---|---|
action |
"clicked" | "closed" | "expired" |
Required. Specifies whether the user has clicked a button or closed the dialog, or the dialog timer has expired. |
button |
string |
ID of the clicked button. The default dialogs use "affirmative", "negative", and "cancel". |
value |
string |
Value of the dialog input. |
The <DialogHeader />, <DialogBody />, <DialogFooter />, and <DialogButton /> components of IODialogs render the default dialog sections and buttons, so you can combine them with your own content. The <DialogButton /> component has an id prop for the button ID and an onButtonFocused prop that accepts a callback invoked with the button ID when the button receives focus.
When the current Global Layout has unsaved changes, the default dialog app shows a dialog with "Save", "Discard", and "Cancel" buttons before shutting down or restarting the platform, or restoring another Global Layout. If you handle this case in your dialog app, return "save-changes", "discard-changes", or "go-back" as the button ID.
The following example demonstrates creating a dialog with a custom body for the unsaved Layout changes:
import { ButtonGroup, IODialogs } from "@interopio/components-react";
const {
Dialog,
DialogBody,
DialogButton,
DialogFooter,
useDialogsContext
} = IODialogs;
const LAYOUT_OPERATIONS = ["systemShutdown", "systemRestart", "layoutRestore"];
const LayoutModifiedDialog = ({ config, setResult }) => {
const body = (
<DialogBody>
<p>The current Layout has unsaved changes. Do you want to save them?</p>
</DialogBody>
);
const footer = (
<DialogFooter>
<ButtonGroup align="right">
<DialogButton id="save-changes" variant="primary" text="Save" onClick={() => setResult({ action: "clicked", button: "save-changes" })} />
<DialogButton id="discard-changes" text="Discard" onClick={() => setResult({ action: "clicked", button: "discard-changes" })} />
<DialogButton id="go-back" text="Cancel" onClick={() => setResult({ action: "clicked", button: "go-back" })} />
</ButtonGroup>
</DialogFooter>
);
return <Dialog config={config} setResult={setResult} body={body} footer={footer} />;
};
const DialogApp = () => {
const { config, setResult } = useDialogsContext();
if (config.context?.isLayoutModified && LAYOUT_OPERATIONS.includes(config.operation)) {
return <LayoutModifiedDialog config={config} setResult={setResult} />;
}
return <Dialog config={config} setResult={setResult} />;
};Template Dialogs
Available since io.Connect Desktop 10.2 & @interopio/components-react 4.3
The <Dialogs /> component of IODialogTemplates renders the dialogs requested via the request() method of the Dialogs API. It selects a dialog template by the templateName property of the dialog request and renders the template component. It has the following props:
| Prop | Type | Description |
|---|---|---|
messagePort |
object |
Required. Object that delivers the dialog requests from the platform and sends the responses back to it. Pass the ioDialog object injected in the global window object. |
templates |
object[] |
Required. Available dialog templates. |
Each object in the templates array has the following properties:
| Property | Type | Description |
|---|---|---|
Dialog |
React.ComponentType |
Required. Component for rendering the dialog. |
name |
string |
Required. Name of the template. Must match the templateName property of the dialog request. |
validate |
function |
Required. Accepts the dialog request and returns it if it's valid. |
The DEFAULT_DIALOG_TEMPLATES array of IODialogTemplates contains the templates for the predefined dialogs - "noInputsConfirmationDialog", "singleCheckboxDialog", and "singleTextInputDialog". Their components are also available as the <NoInputsConfirmationDialog />, <SingleCheckboxDialog />, and <SingleTextInputDialog /> components of IODialogTemplates.
The template component receives the following props:
| Prop | Type | Description |
|---|---|---|
onCompletion |
function |
Callback for sending the response to the client app. Accepts a DialogResponse object. |
size |
object |
Size of the dialog as specified in the dialog request. |
variables |
object |
Variables passed by the client app in the dialog request. |
The following example demonstrates adding a custom dialog template to the default ones:
import { IODialogTemplates } from "@interopio/components-react";
const { Dialogs, DEFAULT_DIALOG_TEMPLATES } = IODialogTemplates;
const MyCustomDialog = ({ variables, onCompletion }) => {
return (
<div className="my-dialog">
<h1>{variables.title}</h1>
<p>{variables.text}</p>
<button onClick={() => onCompletion({ responseButtonClicked: { id: "confirm", text: "Confirm" } })}>Confirm</button>
<button onClick={() => onCompletion({ isClosed: true })}>Close</button>
</div>
);
};
const templates = [
...DEFAULT_DIALOG_TEMPLATES,
{
name: "MyCustomDialog",
Dialog: MyCustomDialog,
validate: config => config
}
];
const TemplateDialogs = () => <Dialogs messagePort={window.ioDialog} templates={templates} />;Configuration
To use a custom dialog app, set the "url" property of the "dialogs" top-level key in the system.json system configuration file of io.Connect Desktop to the location of your custom dialog app:
{
"dialogs": {
"url": "https://my-custom-dialog.com"
}
}When an interop-enabled app invokes the request() method of the Dialogs API, io.Connect Desktop will load the custom dialog app instead of the default one. The platform also loads the custom dialog app for its own dialogs - e.g., when the platform is about to shut down or restart, or when the user tries to close a window that is prevented from closing.
Styles
To customize the styles of the io.Connect modal windows, use the CSS variables provided by the io.Connect themes.