Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add options for node menu #4354

Merged
merged 15 commits into from
Aug 6, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,45 @@
aria-controls="overlay_menu"
severity="secondary"
/>
<Menu ref="menu" id="overlay_menu" :model="menuItems" :popup="true" />
<Menu
ref="menu"
id="overlay_menu"
:model="menuItems"
:popup="true"
@mouseenter="emit('menu-mouseenter')"
@mouseleave="emit('menu-mouseleave')"
@focus="() => {}"
@blur="() => {}"
asylves1 marked this conversation as resolved.
Show resolved Hide resolved
asylves1 marked this conversation as resolved.
Show resolved Hide resolved
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ref, onMounted } from 'vue';
import Menu from 'primevue/menu';
import Button from 'primevue/button';
import { OperatorMenuItem } from '@/services/workflow';

const isMenuShowing = ref<boolean>(false);
const props = defineProps<{
nodeMenu: OperatorMenuItem[];
}>();

const emit = defineEmits(['menu-mouseenter', 'menu-mouseleave', 'menu-selection']);

const isMenuShowing = ref<boolean>(false);
const menu = ref();
const menuItems = ref([
{
// label: "Options",
items: [
{
label: 'Configure Model',
command() {}
},
{
label: 'Stratify Model',
command() {}
},
{
label: 'Edit Model',
command() {}
const menuItems = ref();

onMounted(() => {
const options: Array<{}> = [];
props.nodeMenu.forEach((node) =>
options.push({
label: node.displayName,
command() {
emit('menu-selection', node.type);
}
]
}
]);
})
);
menuItems.value = [{ items: options }];
});

function showMenu(event) {
menu.value.show(event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,17 @@
/>
</main>
<tera-operator-menu
v-if="isMenuFocused"
v-if="isMenuFocused && menuOptions.length"
:nodeMenu="menuOptions"
:style="{
top: '-30px',
left: `${operatorPosition.width + 20}px`
}"
@mouseenter="showOutputButton(PortType.Output)"
@mouseleave="hideOutputButton"
@menu-mouseenter="showOutputButton(PortType.Output)"
@menu-mouseleave="hideOutputButton"
@menu-selection="(operatorType) => onSelection(operatorType)"
@focus="() => {}"
@blur="() => {}"
/>
Expand All @@ -68,15 +72,18 @@ import TeraOperatorInputs from '@/components/operator/tera-operator-inputs.vue';
import TeraOperatorOutputs from '@/components/operator/tera-operator-outputs.vue';
import TeraOperatorAnnotation from '@/components/operator/tera-operator-annotation.vue';
import TeraOperatorMenu from '@/components/operator/tera-operator-menu.vue';
import { OperatorMenuItem } from '@/services/workflow';

const props = defineProps<{
node: WorkflowNode<any>;
nodeMenu: Map<string, OperatorMenuItem[]>;
}>();

const emit = defineEmits([
'port-selected',
'port-mouseover',
'port-mouseleave',
'menu-selection',
'remove-operator',
'remove-edges',
'resize',
Expand All @@ -96,6 +103,7 @@ const annotationRef = ref<typeof TeraOperatorAnnotation | null>(null);

const isMenuFocused = ref<boolean>(false);
const menuButtonTimeoutId = ref<NodeJS.Timeout | null>(null);
const menuOptions = ref<OperatorMenuItem[] | []>([]);

let resizeObserver: ResizeObserver | null = null;

Expand Down Expand Up @@ -143,11 +151,16 @@ function mouseleavePort() {
emit('port-mouseleave');
}

function onSelection(operatorType: string) {
emit('menu-selection', operatorType);
}

function resizeHandler() {
emit('resize', props.node);
}

onMounted(() => {
menuOptions.value = props.nodeMenu.get(props.node.operationType) ?? [];
if (operator.value) {
resizeObserver = new ResizeObserver(resizeHandler);
resizeObserver.observe(operator.value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
<tera-operator
ref="teraOperatorRefs"
:node="node"
:nodeMenu="nodeMenu"
@resize="resizeHandler"
@port-selected="(port: WorkflowPort, direction: WorkflowDirection) => createNewEdge(node, port, direction)"
@port-mouseover="onPortMouseover"
Expand All @@ -76,6 +77,7 @@
@duplicate-branch="duplicateBranch(node.id)"
@remove-edges="removeEdges"
@update-state="(event: any) => updateWorkflowNodeState(node, event)"
@menu-selection="(operatorType) => onMenuSelection(operatorType, node)"
>
<template #body>
<component
Expand Down Expand Up @@ -174,7 +176,7 @@ import InputText from 'primevue/inputtext';
import Menu from 'primevue/menu';
import ContextMenu from 'primevue/contextmenu';
import * as workflowService from '@/services/workflow';
import { OperatorImport, OperatorNodeSize } from '@/services/workflow';
import { OperatorImport, OperatorNodeSize, getNodeMenu } from '@/services/workflow';
import * as d3 from 'd3';
import { AssetType, EventType } from '@/types/Types';
import { useDragEvent } from '@/services/drag-drop';
Expand Down Expand Up @@ -237,6 +239,8 @@ const props = defineProps<{
assetId: string;
}>();

const nodeMenu = ref(getNodeMenu(registry.operationMap));

const route = useRoute();
const router = useRouter();

Expand Down Expand Up @@ -439,6 +443,21 @@ const addOperatorToWorkflow: Function =
workflowDirty = true;
};

function onMenuSelection(operatorType: string, menuNode: WorkflowNode<any>) {
const name = operatorType;
const operation = registry.getOperation(operatorType);
const node = registry.getNode(operatorType);
const drilldown = registry.getDrilldown(operatorType);

const width = workflowService.getOperatorNodeSize(OperatorNodeSize.medium).width;
newNodePosition.x = menuNode.x + 80 + width;
newNodePosition.y = menuNode.y;

if (name && operation && node && drilldown) {
addOperatorToWorkflow({ name, operation, node, drilldown })();
}
}

// Menu categories and list items are in order of appearance for separators to work
const contextMenuItems: MenuItem[] = [
{
Expand Down
71 changes: 70 additions & 1 deletion packages/client/hmi-client/src/services/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { EventEmitter } from '@/utils/emitter';
import type { Position } from '@/types/common';
import type {
Operation,
OperationData,
Size,
Workflow,
WorkflowEdge,
Expand Down Expand Up @@ -43,7 +44,7 @@ export enum OperatorNodeSize {
xlarge
}

function getOperatorNodeSize(size: OperatorNodeSize): Size {
export function getOperatorNodeSize(size: OperatorNodeSize): Size {
switch (size) {
case OperatorNodeSize.small:
return { width: 140, height: 220 };
Expand Down Expand Up @@ -634,3 +635,71 @@ export const branchWorkflow = (wf: Workflow, nodeId: string) => {
wf.nodes.push(...copyNodes);
wf.edges.push(...copyEdges);
};

export interface OperatorMenuItem {
type: string;
displayName: string;
}

function getInputMap(operationMap: Map<string, Operation>) {
const inputMap = new Map<string, OperatorMenuItem[]>();

operationMap.forEach((value, key) => {
const inputList: Array<OperationData> = value?.inputs ?? [];

const inputSubList: OperationData[] = [];
inputList.forEach((input) => {
if (input.type.includes('|')) {
input.type.split('|').forEach((type) => inputSubList.push({ type }));
} else {
inputSubList.push(input);
}
});

inputSubList.forEach((input) => {
const displayName: string = value.displayName ?? key;
if (inputMap.has(input.type)) {
const list = inputMap.get(input.type) ?? [];
list.push({ type: key, displayName });
inputMap.set(input.type, list);
} else {
inputMap.set(input.type, [{ type: key, displayName }]);
}
});
});
return inputMap;
}

function getOutputMap(operationMap: Map<string, Operation>) {
const outputMap = new Map<string, string[]>();

operationMap.forEach((value, key) => {
const outputList: OperationData[] = value?.outputs ?? [];

outputList.forEach((input) => {
if (outputMap.has(key)) {
const list = outputMap.get(key) ?? [];
list.push(input.type);
outputMap.set(key, list);
} else {
outputMap.set(key, [input.type]);
}
});
});
return outputMap;
}
asylves1 marked this conversation as resolved.
Show resolved Hide resolved

export function getNodeMenu(operationMap: Map<string, Operation>) {
const menuOptions = new Map<string, OperatorMenuItem[]>();

const inputMap = getInputMap(operationMap);
const outputMap = getOutputMap(operationMap);

const uniqInputMap: Map<string, OperatorMenuItem[]> = new Map();
inputMap.forEach((value, key) => uniqInputMap.set(key, _.uniqBy(value, 'type')));

outputMap.forEach((value, key) => {
menuOptions.set(key, uniqInputMap.get(value[0]) ?? []);
});
return menuOptions;
}
Loading