mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-19 02:21:32 +08:00
Rewrite masthead as composition, replace logo/text with logo only
This commit is contained in:
@@ -1,10 +1,102 @@
|
||||
<script setup>
|
||||
import { BNavbar, BNavbarBrand, BNavbarNav } from "bootstrap-vue";
|
||||
import MastheadItem from "./MastheadItem";
|
||||
import { loadWebhookMenuItems } from "./_webhooks";
|
||||
import QuotaMeter from "./QuotaMeter";
|
||||
import { safePath } from "utils/redirect";
|
||||
import { getActiveTab } from "./utilities";
|
||||
import { getGalaxyInstance } from "app";
|
||||
import { watch, computed, ref } from "vue";
|
||||
import { defineEmits, defineProps, onMounted } from "vue";
|
||||
import { useRoute } from "vue-router/composables";
|
||||
|
||||
// basics
|
||||
const route = useRoute();
|
||||
const emit = defineEmits(["open-url"]);
|
||||
|
||||
/* props */
|
||||
const props = defineProps({
|
||||
tabs: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
brand: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
initialActiveTab: {
|
||||
type: String,
|
||||
default: "analysis",
|
||||
},
|
||||
logoUrl: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
logoSrc: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
logoSrcSecondary: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
windowTab: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
/* refs */
|
||||
const activeTab = ref(props.initialActiveTab);
|
||||
const extensionTabs = ref([]);
|
||||
const windowToggle = ref(false);
|
||||
|
||||
/* computed */
|
||||
const allTabs = computed(() => [].concat(props.tabs, extensionTabs.value));
|
||||
const toolBoxProperties = computed(() => {
|
||||
const Galaxy = getGalaxyInstance();
|
||||
return {
|
||||
storedWorkflowMenuEntries: Galaxy.config.stored_workflow_menu_entries,
|
||||
};
|
||||
});
|
||||
|
||||
/* methods */
|
||||
function setActiveTab() {
|
||||
const currentRoute = route.path;
|
||||
activeTab.value = getActiveTab(currentRoute, props.tabs) || activeTab.value;
|
||||
}
|
||||
function onWindowToggle() {
|
||||
windowToggle.value = !windowToggle.value;
|
||||
}
|
||||
|
||||
/* watchers */
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
setActiveTab();
|
||||
}
|
||||
);
|
||||
|
||||
/* lifecyle */
|
||||
onMounted(() => {
|
||||
loadWebhookMenuItems(extensionTabs.value);
|
||||
setActiveTab();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<b-navbar id="masthead" type="dark" role="navigation" aria-label="Main" class="justify-content-center">
|
||||
<b-navbar-brand :href="getPath(logoUrl)" aria-label="homepage">
|
||||
<img alt="logo" class="navbar-brand-image" :src="getPath(logoSrc)" />
|
||||
<img v-if="logoSrcSecondary" alt="logo" class="navbar-brand-image" :src="getPath(logoSrcSecondary)" />
|
||||
<span class="navbar-brand-title">{{ brandTitle }}</span>
|
||||
</b-navbar-brand>
|
||||
<b-navbar id="masthead" type="dark" role="navigation" aria-label="Main" class="justify-content-between">
|
||||
<b-navbar-nav>
|
||||
<b-navbar-brand id="analysis" :href="safePath(logoUrl)" aria-label="homepage">
|
||||
<b-button variant="link" size="sm" title="Galaxy" v-b-tooltip.hover>
|
||||
<img alt="logo" :src="safePath(logoSrc)" />
|
||||
<img v-if="logoSrcSecondary" alt="logo" :src="safePath(logoSrcSecondary)" />
|
||||
</b-button>
|
||||
</b-navbar-brand>
|
||||
<b-nav-item v-if="brand" class="navbar-brand-title" disabled>
|
||||
{{ brand }}
|
||||
</b-nav-item>
|
||||
</b-navbar-nav>
|
||||
<b-navbar-nav>
|
||||
<masthead-item
|
||||
v-for="(tab, idx) in allTabs"
|
||||
@@ -12,106 +104,9 @@
|
||||
:key="`tab-${idx}`"
|
||||
:tab="tab"
|
||||
:active-tab="activeTab"
|
||||
@open-url="$emit('open-url', $event)" />
|
||||
@open-url="emit('open-url', $event)" />
|
||||
<masthead-item v-if="windowTab" :tab="windowTab" :toggle="windowToggle" @click="onWindowToggle" />
|
||||
</b-navbar-nav>
|
||||
<quota-meter />
|
||||
</b-navbar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BNavbar, BNavbarBrand, BNavbarNav } from "bootstrap-vue";
|
||||
import MastheadItem from "./MastheadItem";
|
||||
import { loadWebhookMenuItems } from "./_webhooks";
|
||||
import QuotaMeter from "./QuotaMeter.vue";
|
||||
import { safePath } from "utils/redirect";
|
||||
import { getActiveTab } from "./utilities";
|
||||
|
||||
export default {
|
||||
name: "Masthead",
|
||||
components: {
|
||||
BNavbar,
|
||||
BNavbarBrand,
|
||||
BNavbarNav,
|
||||
MastheadItem,
|
||||
QuotaMeter,
|
||||
},
|
||||
props: {
|
||||
displayGalaxyBrand: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
tabs: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
brand: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
initialActiveTab: {
|
||||
type: String,
|
||||
default: "analysis",
|
||||
},
|
||||
logoUrl: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
logoSrc: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
logoSrcSecondary: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
windowTab: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: this.initialActiveTab,
|
||||
extensionTabs: [],
|
||||
windowToggle: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
brandTitle() {
|
||||
let brandTitle = this.displayGalaxyBrand ? "Galaxy " : "";
|
||||
if (this.brand) {
|
||||
brandTitle += this.brand;
|
||||
}
|
||||
return brandTitle;
|
||||
},
|
||||
allTabs() {
|
||||
return [].concat(this.tabs, this.extensionTabs);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.updateTab();
|
||||
},
|
||||
},
|
||||
created() {
|
||||
loadWebhookMenuItems(this.extensionTabs);
|
||||
this.updateTab();
|
||||
},
|
||||
methods: {
|
||||
addItem(item) {
|
||||
this.allTabs.push(item);
|
||||
},
|
||||
getPath(url) {
|
||||
return safePath(url);
|
||||
},
|
||||
updateTab() {
|
||||
const currentRoute = this.$route?.path;
|
||||
this.activeTab = getActiveTab(currentRoute, this.tabs) || this.activeTab;
|
||||
},
|
||||
onWindowToggle() {
|
||||
this.windowToggle = !this.windowToggle;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,3 +1,77 @@
|
||||
<script setup>
|
||||
import Vue from "vue";
|
||||
import { VBPopoverPlugin, VBTooltipPlugin } from "bootstrap-vue";
|
||||
import { BNavItem, BNavItemDropdown, BDropdownItem } from "bootstrap-vue";
|
||||
import { safePath } from "utils/redirect";
|
||||
import { ref, computed } from "vue";
|
||||
import { getCurrentInstance } from "vue";
|
||||
|
||||
Vue.use(VBPopoverPlugin);
|
||||
Vue.use(VBTooltipPlugin);
|
||||
|
||||
const instance = getCurrentInstance().proxy;
|
||||
const emit = defineEmits(["click", "open-url"]);
|
||||
const dropdown = ref(null);
|
||||
|
||||
/* props */
|
||||
const props = defineProps({
|
||||
tab: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
toggle: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
activeTab: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
/* computed */
|
||||
const menu = computed(() => props.tab.menu);
|
||||
const popoverNote = computed(
|
||||
() => `Please <a href="${safePath("/login")}">log in or register</a> to use this feature.`
|
||||
);
|
||||
const classes = computed(() => {
|
||||
const isActiveTab = props.tab.id == props.activeTab;
|
||||
return Object.fromEntries([
|
||||
["active", isActiveTab],
|
||||
[props.tab.cls, true],
|
||||
]);
|
||||
});
|
||||
const linkClasses = computed(() => ({
|
||||
"nav-icon": props.tab.icon,
|
||||
toggle: props.toggle,
|
||||
}));
|
||||
const iconClasses = computed(() =>
|
||||
Object.fromEntries([
|
||||
["fa fa-fw", true],
|
||||
[props.tab.icon, props.tab.icon],
|
||||
])
|
||||
);
|
||||
|
||||
/* methods */
|
||||
function open(tab, event) {
|
||||
if (tab.onclick) {
|
||||
event.preventDefault();
|
||||
tab.onclick();
|
||||
emit("click");
|
||||
} else if (tab.disabled) {
|
||||
event.preventDefault();
|
||||
instance.$root.$emit("bv::hide::tooltip");
|
||||
instance.$root.$emit("bv::show::popover", tab.id);
|
||||
setTimeout(() => {
|
||||
instance.$root.$emit("bv::hide::popover", tab.id);
|
||||
}, 3000);
|
||||
} else if (!tab.menu) {
|
||||
event.preventDefault();
|
||||
emit("open-url", { ...tab });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<b-nav-item
|
||||
v-if="!menu"
|
||||
@@ -5,7 +79,7 @@
|
||||
v-b-tooltip.hover.bottom
|
||||
v-b-popover.manual.bottom="{ id: tab.id, content: popoverNote, html: true }"
|
||||
:class="classes"
|
||||
:href="getPath(tab.url)"
|
||||
:href="safePath(tab.url)"
|
||||
:target="tab.target || '_parent'"
|
||||
:link-classes="linkClasses"
|
||||
:title="tab.tooltip"
|
||||
@@ -31,12 +105,16 @@
|
||||
href="#"
|
||||
:title="tab.tooltip"
|
||||
@show="open(tab, $event)">
|
||||
<template v-if="tab.icon" v-slot:button-content>
|
||||
<span class="sr-only">{{ tab.tooltip || tab.id }}</span>
|
||||
<span :class="iconClasses" />
|
||||
</template>
|
||||
<template v-for="(item, idx) in tab.menu">
|
||||
<div v-if="item.divider" :key="`divider-${idx}`" class="dropdown-divider" />
|
||||
<b-dropdown-item
|
||||
v-else-if="item.hidden !== true"
|
||||
:key="`item-${idx}`"
|
||||
:href="getPath(item.url)"
|
||||
:href="safePath(item.url)"
|
||||
:target="item.target || '_parent'"
|
||||
role="menuitem"
|
||||
:active="item.disabled"
|
||||
@@ -47,96 +125,3 @@
|
||||
</template>
|
||||
</b-nav-item-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from "vue";
|
||||
import { VBPopoverPlugin, VBTooltipPlugin } from "bootstrap-vue";
|
||||
import { BNavItem, BNavItemDropdown, BDropdownItem } from "bootstrap-vue";
|
||||
import { safePath } from "utils/redirect";
|
||||
|
||||
Vue.use(VBPopoverPlugin);
|
||||
Vue.use(VBTooltipPlugin);
|
||||
|
||||
export default {
|
||||
name: "MastheadItem",
|
||||
components: {
|
||||
BNavItem,
|
||||
BNavItemDropdown,
|
||||
BDropdownItem,
|
||||
},
|
||||
props: {
|
||||
tab: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
toggle: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
activeTab: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
menu() {
|
||||
return this.tab.menu;
|
||||
},
|
||||
popoverNote() {
|
||||
return `Please <a href="${safePath("/login")}">log in or register</a> to use this feature.`;
|
||||
},
|
||||
classes() {
|
||||
const isActiveTab = this.tab.id == this.activeTab;
|
||||
return Object.fromEntries([
|
||||
["active", isActiveTab],
|
||||
[this.tab.cls, true],
|
||||
]);
|
||||
},
|
||||
linkClasses() {
|
||||
return {
|
||||
"nav-icon": this.tab.icon,
|
||||
toggle: this.toggle,
|
||||
};
|
||||
},
|
||||
iconClasses() {
|
||||
return Object.fromEntries([
|
||||
["fa fa-fw", true],
|
||||
[this.tab.icon, this.tab.icon],
|
||||
]);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener("blur", this.hideDropdown);
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener("blur", this.hideDropdown);
|
||||
},
|
||||
methods: {
|
||||
hideDropdown() {
|
||||
if (this.$refs.dropdown) {
|
||||
this.$refs.dropdown.hide();
|
||||
}
|
||||
},
|
||||
getPath(url) {
|
||||
return safePath(url);
|
||||
},
|
||||
open(tab, event) {
|
||||
if (tab.onclick) {
|
||||
event.preventDefault();
|
||||
tab.onclick();
|
||||
this.$emit("click");
|
||||
} else if (tab.disabled) {
|
||||
event.preventDefault();
|
||||
this.$root.$emit("bv::hide::tooltip");
|
||||
this.$root.$emit("bv::show::popover", tab.id);
|
||||
setTimeout(() => {
|
||||
this.$root.$emit("bv::hide::popover", tab.id);
|
||||
}, 3000);
|
||||
} else if (!tab.menu) {
|
||||
event.preventDefault();
|
||||
this.$emit("open-url", { ...tab });
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -80,7 +80,7 @@ export default {
|
||||
@import "theme/blue.scss";
|
||||
|
||||
.quota-meter {
|
||||
position: absolute;
|
||||
position: relative;
|
||||
right: 0.8rem;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
|
||||
@@ -366,23 +366,22 @@ body {
|
||||
}
|
||||
}
|
||||
.navbar-brand {
|
||||
@extend .position-absolute;
|
||||
@extend .font-weight-bold;
|
||||
@extend .ml-2;
|
||||
@extend .mr-1;
|
||||
button {
|
||||
@extend .p-0;
|
||||
img {
|
||||
display: inline;
|
||||
border: none;
|
||||
height: 2.22rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.navbar-brand-title {
|
||||
font-weight: bold;
|
||||
font-family: Verdana, sans-serif;
|
||||
font-size: 1.25rem;
|
||||
left: 0.8rem;
|
||||
line-height: 0rem;
|
||||
color: $brand-masthead-text-active;
|
||||
text-decoration: none;
|
||||
.navbar-brand-image {
|
||||
display: inline;
|
||||
margin-right: 0.3rem;
|
||||
border: none;
|
||||
max-height: 2rem;
|
||||
}
|
||||
.navbar-brand-title {
|
||||
color: $brand-masthead-text;
|
||||
}
|
||||
font-size: calc($masthead-height / 3);
|
||||
line-height: calc($masthead-height / 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 569 B After Width: | Height: | Size: 18 KiB |
Reference in New Issue
Block a user