index.vue
3.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<script setup lang="ts">
import { computed, CSSProperties, unref } from 'vue';
import { NodeData } from '../../../types/node';
import { RuleChainContextMenuItemType } from './index.type';
import { Icon } from '/@/components/Icon';
import { Divider } from 'ant-design-vue';
const props = withDefaults(
defineProps<{
width?: number;
itemHeight?: number;
styles?: CSSProperties;
axis?: Record<'x' | 'y', number>;
items?: RuleChainContextMenuItemType[];
nodeData?: NodeData;
}>(),
{
width: 320,
itemHeight: 48,
axis: () => ({ x: 0, y: 0 }),
items: () => [],
nodeData: () => ({}),
}
);
const getMenuHeight = computed(() => {
const { items, itemHeight } = props;
let dividerNumber = 0;
let menuNumber = 1;
for (const item of items) {
if (item.divider) dividerNumber++;
else menuNumber++;
}
return itemHeight * menuNumber + dividerNumber + 8;
});
const getStyle = computed((): CSSProperties => {
const { axis, styles, width } = props;
const { x, y } = axis;
const menuHeight = unref(getMenuHeight);
const menuWidth = width;
const body = document.body;
const left = body.clientWidth < x + menuWidth ? x - menuWidth : x;
const top = body.clientHeight < y + menuHeight ? y - menuHeight : y;
return {
...styles,
position: 'absolute',
width: `${width}px`,
left: `${left + 1}px`,
top: `${top + 1}px`,
};
});
const getNodeIconUrl = computed(() => {
const { config } = props.nodeData;
const { configurationDescriptor } = config || {};
const { nodeDefinition } = configurationDescriptor || {};
const { iconUrl } = nodeDefinition || {};
return iconUrl;
});
const getNodeIcon = computed(() => {
const { nodeData } = props;
const { categoryConfig, config } = nodeData;
const { icon: categoryIcon } = categoryConfig || {};
const { configurationDescriptor } = config || {};
const { nodeDefinition } = configurationDescriptor || {};
const { icon } = nodeDefinition || {};
return categoryIcon || icon;
});
const getTitleBackgroundColor = computed(() => {
const { categoryConfig, config } = props.nodeData;
const { backgroundColor: categoryBackgroundColor } = categoryConfig || {};
const { backgroundColor } = config || {};
return categoryBackgroundColor || backgroundColor;
});
</script>
<template>
<div
:style="getStyle"
class="bg-light-50 shadow-lg shadow-dark-50 z-50 rounded-md overflow-hidden pb-2"
>
<div
v-if="nodeData"
:style="{ backgroundColor: getTitleBackgroundColor, height: `${itemHeight}px` }"
class="flex items-center p-2"
>
<Icon v-if="!getNodeIconUrl" :icon="getNodeIcon" class="svg:text-2xl" />
<img v-if="getNodeIconUrl" :src="getNodeIconUrl" class="w-6 h-6" />
<div class="ml-4">
<div class="font-medium">{{ nodeData.config?.name }}</div>
<div class="text-xs">{{ nodeData.data?.name }}</div>
</div>
</div>
<div>
<template v-for="item in items" :key="item.key">
<div
v-if="!item.divider"
:style="{ height: `${itemHeight}px` }"
class="px-4 flex items-center cursor-pointer hover:bg-neutral-100"
:class="item.disabled && 'disables'"
@click="(event) => !item.disabled && item?.handler?.(event)"
>
<Icon :icon="item.icon" class="svg:text-2xl" />
<div class="flex-auto px-4">{{ item.label }}</div>
<div class="flex items-center"> {{ item.shortcutKey }} </div>
</div>
<Divider v-if="item.divider" class="!m-0" />
</template>
</div>
</div>
</template>
<style lang="less" scoped>
.disables {
@apply pointer-events-none text-gray-300;
}
</style>