Rewrite masthead as composition, replace logo/text with logo only

This commit is contained in:
guerler
2022-11-30 17:19:07 -05:00
parent 22682a9795
commit 95dadf3491
5 changed files with 195 additions and 216 deletions
+99 -104
View File
@@ -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>
+80 -95
View File
@@ -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%;
+15 -16
View File
@@ -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