Navigation Button v1.0
The <e-navigation-button> component is a component that can be used for .
Usage
An e-navigation-button has attributes that must be provided,
<e-navigation-button
:menu-item=""
:is-opened=""
:is-mobile=""
@toggle=""
@toggle:nav-menu=""
/>
Attributes
menuItem | Type: Array | required
isOpened | Type: Boolean | Default:false
isMobile | Type: Boolean | Default:false
@toggle | Type: Function | required
@toggle:nav-menu | Type: Function | required
Examples
Below a few interactive examples of e-navigation-button can be found.
Default
const menuItem = {
displayText: 'Standard Item',
icon: ['heart'],
url: 'https://econnect.eu/',
key: '1'
}
<e-navigation-button
:menu-item='menuItem'
/>
Highlighted Button
const menuItem = {
displayText: 'Highlighted Item',
icon: ['star'],
type: 'highlight',
url: 'https://econnect.eu/',
key: '2'
}
<e-navigation-button
:menu-item='menuItem'
/>
Disabled Button
const menuItem = {
displayText: 'Disabled Item',
disabled: true,
icon: ['close'],
url: 'https://econnect.eu/',
key: '3'
}
<e-navigation-button
:menu-item='menuItem'
/>
Source Code
e-navigation-button.vue
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { RouteLocation, useRouter } from 'vue-router';
import { isEmpty } from 'lodash-es';
import { EMenuItem } from 'types/menu';
import ENavigationButtonDisplay from '../e-navigation-button-display/e-navigation-button-display.vue';
import ENavigationButtonItem from '../e-navigation-button-item/e-navigation-button-item.vue';
export type Props = {
menuItem: EMenuItem;
route: RouteLocation;
isOpened?: boolean;
isMobile?: boolean;
};
const props = withDefaults(defineProps<Props>(), {
isOpened: false,
isMobile: false
});
const emit = defineEmits<{
(e: 'toggle'): void;
(e: 'toggle:nav-menu'): void;
}>();
const router = useRouter();
const navigationItem = computed(() => {
return props.menuItem;
});
const subMenuItems = computed(() => {
return navigationItem.value?.menuItems ?? [];
});
const hasSubItems = computed(() => {
return !isEmpty(subMenuItems.value);
});
const localOpen = ref(props.isOpened);
const isHighlight = computed(() => {
return navigationItem.value?.type === 'highlight';
});
const isActive = computed(() => {
let result: boolean;
const encodedRoute = encodeURI(navigationItem.value?.url);
const encodedCurrentRoute = encodeURI(props.route?.fullPath);
switch (navigationItem.value?.url) {
case '/':
// Home requires exact match
result = encodedRoute === encodedCurrentRoute;
break;
default:
result = encodedCurrentRoute.includes(encodedRoute);
}
return result || navigationItem.value?.active;
});
const isDisabled = computed(() => {
return navigationItem.value?.disabled === true;
});
const isOpen = computed(() => {
if (hasSubItems.value) {
return isActive.value || showSubItems.value || localOpen.value;
} else {
return false;
}
});
const showSubItems = computed(() => {
if (hasSubItems.value) {
return props.isMobile ? isActive.value || localOpen.value : isActive.value;
} else {
return false;
}
});
const classes = computed(() => {
return {
active: isActive.value,
disabled: isDisabled.value,
open: isOpen.value || showSubItems.value,
highlight: isHighlight.value
};
});
function clickedIcon() {
clickedToggle();
}
function clickedToggle() {
if (subMenuItems?.value.length > 0) {
localOpen.value = !localOpen.value;
emit('toggle');
}
}
function clicked() {
emit('toggle:nav-menu');
router.push(navigationItem.value?.url);
}
watch(
() => navigationItem.value?.active,
newValue => {
localOpen.value = newValue === true;
},
{
deep: true,
immediate: true
}
);
</script>
<template>
<div
class="e-navigation-button"
:class="classes"
>
<e-navigation-button-display
:class="{
active: classes['active']
}"
:menu-item="navigationItem"
:is-highlight="isHighlight"
:is-open="isOpen"
:is-mobile="isMobile"
:route="route"
@click:text="clicked"
@click:icon="clickedIcon"
@click:toggle="clickedToggle"
/>
<div
v-if="subMenuItems.length"
class="e-navigation-button-items"
>
<e-navigation-button-item
v-for="subMenuItem in subMenuItems"
:key="subMenuItem?.key"
:menu-item="subMenuItem"
:route="route"
:router="router"
:parent-key="navigationItem?.displayText"
@toggle:nav-menu="emit('toggle:nav-menu')"
/>
</div>
</div>
</template>
<style scoped lang="scss">
$icon-size: 20px;
@mixin color-nav-button(
$background-color: var(--e-white),
$content-background-color: inherit,
$border-color: transparent
) {
& {
border-color: $border-color;
background-color: $background-color;
}
.e-navigation-button-display {
background-color: $content-background-color;
}
}
.e-navigation-button {
border: transparent 2px solid;
width: 276px;
border-radius: 15px;
margin-right: 25px;
user-select: none;
display: flex;
flex-direction: column;
min-height: calc($icon-size + 24px);
&:not(&.open) {
overflow: hidden;
}
.e-navigation-button-items,
.e-navigation-button-display {
justify-content: space-between;
}
.e-navigation-button-display {
gap: 10px;
padding: 10px 13px;
}
.e-navigation-button-items {
display: none;
flex-direction: column;
padding: 5px 13px;
}
@include color-nav-button();
&.active {
@include color-nav-button(
var(--e-background),
var(--e-navigation-button-background-active),
var(--e-background)
);
}
&.open {
min-height: fit-content;
.e-navigation-button-items {
display: flex;
}
@include color-nav-button(var(--e-background), inherit, transparent);
}
&.highlight {
@include color-nav-button(
var(--e-accent),
var(--e-navigation-button-background-highlight-active) var(--e-accent)
);
&.active {
@include color-nav-button(
var(--e-navigation-button-background-highlight-active),
inherit,
var(--e-accent)
);
}
&.open {
@include color-nav-button((var(--e-accent), 0%), transparent, var(--e-accent));
}
}
&:focus-within,
&:hover {
transition: none;
@include color-nav-button(transparent, var(--e-navigation-button-hover-color), transparent);
&.active {
@include color-nav-button(
var(--e-navigation-button-background-highlight-active),
var(--e-navigation-button-background-highlight-active),
var(--e-accent)
);
}
&.open {
@include color-nav-button(var(--e-background), inherit, transparent);
}
&.highlight {
@include color-nav-button(
var(--e-navigation-button-background-highlight-active),
var(--e-background),
var(--e-accent)
);
}
}
&.disabled {
cursor: not-allowed;
@include color-nav-button(var(--e-background), inherit, transparent);
&.highlight {
@include color-nav-button(transparent, transparent, var(--e-disabled));
}
}
}
</style>