mirror of https://github.com/rancher/dashboard.git
171 lines
3.6 KiB
Vue
171 lines
3.6 KiB
Vue
<script>
|
|
import { mapGetters } from 'vuex';
|
|
import $ from 'jquery';
|
|
import { AUTO, CENTER, fitOnScreen } from '@/utils/position';
|
|
import { isAlternate } from '@/utils/platform';
|
|
|
|
const HIDDEN = 'hide';
|
|
const CALC = 'calculate';
|
|
const SHOW = 'show';
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
phase: HIDDEN,
|
|
style: {}
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
...mapGetters({
|
|
targetElem: 'action-menu/elem',
|
|
targetEvent: 'action-menu/event',
|
|
shouldShow: 'action-menu/showing',
|
|
options: 'action-menu/options'
|
|
}),
|
|
|
|
showing() {
|
|
return this.phase !== HIDDEN;
|
|
},
|
|
|
|
},
|
|
|
|
watch: {
|
|
shouldShow: {
|
|
handler(show) {
|
|
if ( show ) {
|
|
this.phase = CALC;
|
|
this.updateStyle();
|
|
this.$nextTick(() => {
|
|
if ( this.phase === CALC ) {
|
|
this.phase = SHOW;
|
|
this.updateStyle();
|
|
}
|
|
});
|
|
} else {
|
|
this.phase = HIDDEN;
|
|
}
|
|
},
|
|
},
|
|
|
|
'$route.path'(val, old) {
|
|
this.hide();
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
hide() {
|
|
this.$store.commit('action-menu/hide');
|
|
},
|
|
|
|
updateStyle() {
|
|
if ( this.phase === SHOW ) {
|
|
const menu = $('.menu', this.$el)[0];
|
|
const event = this.targetEvent;
|
|
const elem = this.targetElem;
|
|
|
|
this.style = fitOnScreen(menu, event || elem, {
|
|
overlapX: true,
|
|
fudgeX: elem ? 4 : 0,
|
|
fudgeY: elem ? 4 : 0,
|
|
positionX: (elem ? AUTO : CENTER),
|
|
positionY: AUTO,
|
|
});
|
|
|
|
this.style.visibility = 'visible';
|
|
} else {
|
|
this.style = {};
|
|
}
|
|
},
|
|
|
|
execute(action, event, args) {
|
|
const opts = { alt: isAlternate(event) };
|
|
|
|
this.$store.dispatch('action-menu/execute', {
|
|
action, args, opts
|
|
});
|
|
this.hide();
|
|
},
|
|
|
|
hasOptions(options) {
|
|
return options.length !== undefined ? options.length : Object.keys(options).length > 0;
|
|
}
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="showing">
|
|
<div class="background" @click="hide" @contextmenu.prevent></div>
|
|
<ul class="list-unstyled menu" :style="style">
|
|
<li v-for="opt in options" :key="opt.action" :class="{divider: opt.divider}" @click="execute(opt, $event)">
|
|
<i v-if="opt.icon" :class="{icon: true, [opt.icon]: true}" />
|
|
<span v-html="opt.label" />
|
|
</li>
|
|
<li v-if="!hasOptions(options)" class="no-actions">
|
|
<span v-t="'sortableTable.noActions'" />
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.root {
|
|
position: absolute;
|
|
}
|
|
|
|
.menu {
|
|
position: absolute;
|
|
visibility: hidden;
|
|
top: 0;
|
|
left: 0;
|
|
z-index: z-index('dropdownContent');
|
|
|
|
color: var(--dropdown-text);
|
|
background-color: var(--dropdown-bg);
|
|
border: 1px solid var(--dropdown-border);
|
|
border-radius: 5px;
|
|
box-shadow: 0 5px 20px var(--shadow);
|
|
|
|
LI {
|
|
padding: 10px;
|
|
margin: 0;
|
|
|
|
&.divider {
|
|
padding: 0;
|
|
border-bottom: 1px solid var(--dropdown-divider);
|
|
}
|
|
|
|
&:not(.divider):hover {
|
|
background-color: var(--dropdown-hover-bg);
|
|
color: var(--dropdown-hover-text);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.icon {
|
|
display: unset;
|
|
}
|
|
|
|
&.no-actions {
|
|
color: var(--disabled-text);
|
|
}
|
|
|
|
&.no-actions:hover {
|
|
background-color: initial;
|
|
color: var(--disabled-text);
|
|
cursor: default;
|
|
}
|
|
}
|
|
}
|
|
|
|
.background {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
opacity: 0;
|
|
z-index: z-index('dropdownOverlay');
|
|
}
|
|
</style>
|