diff --git a/packages/eez-studio-ui/_stylesheets/app.less b/packages/eez-studio-ui/_stylesheets/app.less
index 116e83448..87f9a60e0 100644
--- a/packages/eez-studio-ui/_stylesheets/app.less
+++ b/packages/eez-studio-ui/_stylesheets/app.less
@@ -21,6 +21,10 @@
user-select: auto;
}
+.modal > .modal-dialog > .modal-content {
+ box-shadow: @modalDialogDropdownShadow;
+}
+
body {
overflow: hidden;
font-size: 0.8rem;
diff --git a/packages/eez-studio-ui/_stylesheets/vars-dark.less b/packages/eez-studio-ui/_stylesheets/vars-dark.less
index ffc59d47e..2deb2df39 100644
--- a/packages/eez-studio-ui/_stylesheets/vars-dark.less
+++ b/packages/eez-studio-ui/_stylesheets/vars-dark.less
@@ -112,3 +112,5 @@
@tipBoxColor: @textColor;
@tipBoxBackgroundColor: #003100;
@tipBoxBorderColor: #009400;
+
+@modalDialogDropdownShadow: 4px 4px 24px rgba(0, 0, 0, 0.8);
diff --git a/packages/eez-studio-ui/_stylesheets/vars.less b/packages/eez-studio-ui/_stylesheets/vars.less
index 16254911f..7cab9f738 100644
--- a/packages/eez-studio-ui/_stylesheets/vars.less
+++ b/packages/eez-studio-ui/_stylesheets/vars.less
@@ -108,3 +108,5 @@
@tipBoxColor: @textColor;
@tipBoxBackgroundColor: #e6f6e6;
@tipBoxBorderColor: #c3e6c3;
+
+@modalDialogDropdownShadow: 4px 4px 16px rgba(64, 64, 64, 0.5);
diff --git a/packages/eez-studio-ui/dialog.tsx b/packages/eez-studio-ui/dialog.tsx
index b049dc8f3..aeb61d6d6 100644
--- a/packages/eez-studio-ui/dialog.tsx
+++ b/packages/eez-studio-ui/dialog.tsx
@@ -34,6 +34,8 @@ export function showDialog(dialog: JSX.Element, opts?: IDialogOptions) {
const root = createRoot(element);
root.render(dialog);
+ let jsPanelDialog;
+
if (opts && opts.jsPanel) {
element.style.position = "absolute";
element.style.width = "100%";
@@ -129,15 +131,28 @@ export function showDialog(dialog: JSX.Element, opts?: IDialogOptions) {
}
}
- const dialog = (opts.jsPanel.modeless ? jsPanel : jsPanel.modal).create(
- config
- );
-
- return [dialog, element, root];
+ jsPanelDialog = (
+ opts.jsPanel.modeless ? jsPanel : jsPanel.modal
+ ).create(config);
} else {
document.body.appendChild(element);
- return [undefined, element, root];
}
+
+ // Unmount dialog if the element is removed from the DOM
+ const intervalID = setInterval(() => {
+ let parentElement = element.parentElement;
+ while (parentElement) {
+ if (parentElement instanceof HTMLBodyElement) {
+ return;
+ }
+ parentElement = parentElement.parentElement;
+ }
+
+ clearInterval(intervalID);
+ root.unmount();
+ }, 100);
+
+ return [jsPanelDialog, element, root];
}
////////////////////////////////////////////////////////////////////////////////
@@ -302,6 +317,7 @@ export const BootstrapDialog = observer(
additionalFooterControl?: React.ReactNode;
backdrop?: "static" | boolean;
className?: string;
+ modalContentStyle?: React.CSSProperties;
}> {
div: HTMLDivElement | null = null;
form: HTMLFormElement | null = null;
@@ -325,21 +341,36 @@ export const BootstrapDialog = observer(
}
};
+ static openBootstrapDialogs: React.Component[] = [];
+
+ get numOpenBootstrapDialogs() {
+ return BootstrapDialog.openBootstrapDialogs.filter(
+ genericDialog => genericDialog !== this
+ ).length;
+ }
+
componentDidMount() {
const div = this.div;
if (div) {
$(div).on("shown.bs.modal", () => {
+ BootstrapDialog.openBootstrapDialogs.push(this);
+
setTimeout(this.setFocus);
});
$(div).on("hidden.bs.modal", () => {
+ BootstrapDialog.openBootstrapDialogs.pop();
+
const parent = div.parentElement as HTMLElement;
parent.remove();
this.props.onCancel();
});
this.modal = new bootstrap.Modal(div, {
- backdrop: this.props.backdrop ?? true
+ backdrop:
+ this.numOpenBootstrapDialogs == 0
+ ? this.props.backdrop ?? true
+ : false
});
this.modal.show();
}
@@ -464,7 +495,14 @@ export const BootstrapDialog = observer(
}}
onKeyPress={this.onKeyPress}
>
-
+
{props.title && (
Promise | boolean | void;
onCancel?: () => void;
onValueChange?: (name: string, value: string) => void;
+ setOnChangeCallback?: (
+ onChange: (fieldProperties: any, value: any) => void
+ ) => void;
}
export const GenericDialog = observer(
@@ -234,6 +237,10 @@ export const GenericDialog = observer(
this.fieldValues = fieldValues;
this.errorMessages = undefined;
+
+ if (this.props.setOnChangeCallback) {
+ this.props.setOnChangeCallback(this.onChange.bind(this));
+ }
}
get values() {
@@ -736,6 +743,9 @@ export function showGenericDialog(conf: {
onOk?: (result: GenericDialogResult) => Promise;
opts?: IDialogOptions;
dialogContext?: any;
+ setOnChangeCallback?: (
+ onChange: (fieldProperties: any, value: any) => void
+ ) => void;
}) {
return new Promise((resolve, reject) => {
const [modalDialog] = showDialog(
@@ -774,6 +784,7 @@ export function showGenericDialog(conf: {
}
reject();
}}
+ setOnChangeCallback={conf.setOnChangeCallback}
/>,
conf.opts
);
diff --git a/packages/project-editor/features/action/action.tsx b/packages/project-editor/features/action/action.tsx
index 05be0b53c..c2f6a4877 100644
--- a/packages/project-editor/features/action/action.tsx
+++ b/packages/project-editor/features/action/action.tsx
@@ -214,16 +214,19 @@ export class Action extends Flow {
type: PropertyType.Enum,
enumItems: [
{
- id: "native"
+ id: "flow"
},
{
- id: "flow"
+ id: "native"
}
],
enumDisallowUndefined: true,
propertyGridGroup: specificGroup,
disabled: (action: Action) => {
- return isNotV1Project(action) && !hasFlowSupport(action);
+ return (
+ (isNotV1Project(action) && !hasFlowSupport(action)) ||
+ isDashboardProject(action)
+ );
}
},
{
@@ -288,6 +291,8 @@ export class Action extends Flow {
}
},
newItem: async (parent: IEezObject) => {
+ const projectStore = getProjectStore(parent);
+
const result = await showGenericDialog({
dialogDefinition: {
title: "New Action",
@@ -300,21 +305,38 @@ export class Action extends Flow {
validators.invalidCharacters("."),
validators.unique({}, parent)
]
+ },
+ {
+ name: "implementationType",
+ type: "enum",
+ enumItems: [
+ {
+ id: "flow",
+ label: "Flow"
+ },
+ {
+ id: "native",
+ label: "Native"
+ }
+ ],
+ visible: () =>
+ !projectStore.projectTypeTraits.isDashboard &&
+ projectStore.projectTypeTraits.hasFlowSupport
}
]
},
- values: {}
+ values: {
+ implementationType: "flow"
+ }
});
- const projectStore = getProjectStore(parent);
-
const actionProperties: Partial = Object.assign(
{
name: result.values.name
},
projectStore.projectTypeTraits.hasFlowSupport
? ({
- implementationType: "flow",
+ implementationType: result.values.implementationType,
components: [],
connectionLine: []
} as Partial)
diff --git a/packages/project-editor/flow/component.tsx b/packages/project-editor/flow/component.tsx
index a2b90dd5a..145bc76c5 100644
--- a/packages/project-editor/flow/component.tsx
+++ b/packages/project-editor/flow/component.tsx
@@ -1,13 +1,16 @@
import { MenuItem } from "@electron/remote";
import React from "react";
-import { observable, computed, makeObservable } from "mobx";
+import { observable, computed, makeObservable, runInAction } from "mobx";
import classNames from "classnames";
import { each } from "lodash";
import { validators } from "eez-studio-shared/validation";
import { BoundingRectBuilder, Point, Rect } from "eez-studio-shared/geometry";
-import { showGenericDialog } from "eez-studio-ui/generic-dialog";
+import {
+ IFieldProperties,
+ showGenericDialog
+} from "eez-studio-ui/generic-dialog";
import * as notification from "eez-studio-ui/notification";
@@ -110,6 +113,7 @@ import { getComponentName } from "project-editor/flow/components/components-regi
import { ProjectEditor } from "project-editor/project-editor-interface";
import { FLOW_ITERATOR_INDEX_VARIABLE } from "project-editor/features/variable/defs";
import type {
+ EnumItems,
IActionComponentDefinition,
IComponentProperty,
IDashboardComponentContext
@@ -164,6 +168,7 @@ import {
} from "project-editor/store/serialization";
import { StylePropertyUI } from "project-editor/features/style/StylePropertyUI";
import { findVariable } from "project-editor/project/project";
+import type { Action } from "project-editor/features/action/action";
////////////////////////////////////////////////////////////////////////////////
@@ -2598,6 +2603,59 @@ export class EventHandler extends EezObject {
return;
}
+ function getActions() {
+ return project.actions.map(action => ({
+ id: action.name,
+ label: action.name
+ }));
+ }
+ const actionEnumItems = observable.box([]);
+ actionEnumItems.set(getActions());
+
+ let onChangeCallback: (fieldProperties: any, value: any) => void;
+
+ const actionProperty: IFieldProperties = {
+ name: "action",
+ type: "enum",
+ enumItems: actionEnumItems,
+ inputGroupButton: (
+
+ ),
+ visible: (values: any) => {
+ return values.handlerType == "action";
+ }
+ };
+
const result = await showGenericDialog({
dialogDefinition: {
title: "New Event Handler",
@@ -2619,17 +2677,7 @@ export class EventHandler extends EezObject {
visible: () =>
project.projectTypeTraits.hasFlowSupport
},
- {
- name: "action",
- type: "enum",
- enumItems: project.actions.map(action => ({
- id: action.name,
- label: action.name
- })),
- visible: (values: any) => {
- return values.handlerType == "action";
- }
- },
+ actionProperty,
{
name: "userData",
type: "number",
@@ -2645,7 +2693,10 @@ export class EventHandler extends EezObject {
: "action",
userData: 0
},
- dialogContext: project
+ dialogContext: project,
+ setOnChangeCallback: callback => {
+ onChangeCallback = callback;
+ }
});
const properties: Partial = {