Settings v1.0
The <e-settings> component is a component that can be used for the settings menu in the top left corner of the eConnect projects, by utilizing the attributes given to the component it is customizable to the project's needs.
Usage
An settings-menu has 2 attributes that must be provided, menuItems and themeOptions
<e-settings
:menuItems="[]"
:themeOptions="{}"
/>
Attributes
| Value | Type | Optional | Default |
|---|---|---|---|
| menuItems | EMenuItem[] | yes | |
| themeOptions | Object | yes |
Sandbox
Examples
Below a few interactive examples of unavailable can be found.
Details
const settingsMenuItems = [{
displayText: 'Language',
key: '0',
icon: ['language'],
menuItems: [
{
displayText: 'Dutch',
subDisplayText: 'Original Translation',
key: '0.1',
icon: ['language'],
emitKey: 'changeLanguage:Dutch'
},
{
displayText: 'English',
key: '0.2',
emitKey: 'changeLanguage:English'
}
]
}, {
displayText: 'Display',
key: '1',
icon: ['dark-mode'],
emitKey: 'changeDisplay'
}, {
displayText: 'Account',
key: '2',
icon: ['account'],
menuItems: [
{
displayText: 'My Profile',
subDisplayText: 'Manage my preferences',
key: '2.1',
icon: ['account'],
disabled: true,
emitKey: 'account:MyProfile'
},
{
displayText: 'Logout',
key: '2.2',
emitKey: 'account:Logout'
}
]
}, {
key: '3',
icon: ['more-vertical'],
emitKey: 'toggleSettingsMenu',
neverHide: true
}]
const themeOptions = {
toggleEnabled: true,
currentTheme: currentTheme.value,
themes: {
light: {
displayText: t('settingsMenu.button.Display.light'),
icon: ['light-mode']
},
dark: {
displayText: t('settingsMenu.button.Display.dark'),
icon: ['dark-mode']
}
},
function: toggleCurrentTheme
}
function toggleCurrentTheme() {
document.querySelector(':root')?.classList.toggle('light')
document.querySelector(':root')?.classList.toggle('dark')
if (process.client) {
darkMode.value.currentTheme = (darkMode.value.currentTheme === 'dark' ? 'light' : 'dark')
localStorage?.setItem('theme', darkMode.value.currentTheme)
}
}
<e-settings
:menuItems="settingsMenuItems"
:themeOptions="themeOptions"
/>
Source Code
e-settings.vue
<script setup lang="ts">
import { computed, onMounted, ref, toRef } from 'vue';
import { useDark } from '@vueuse/core';
import { useI18n } from 'vue-i18n';
import { EMenuItem } from 'types/menu/EMenuItem';
import ESettingsButton from 'components/e-settings-button/e-settings-button.vue';
import ESettingsButtonMenu from 'components/e-settings-button-menu/e-settings-button-menu.vue';
export type Props = {
menuItems: Array<EMenuItem>;
themeOptions?: {
toggleEnabled: boolean;
currentTheme?: string;
themes?: Record<string, { displayText: string; icon: string[] }>;
function?: () => void;
};
};
const props = withDefaults(defineProps<Props>(), {
menuItems: (): EMenuItem[] => {
return [];
},
themeOptions: () => {
return {
toggleEnabled: true
};
}
});
const emit = defineEmits<{
(e: 'button-pressed', key: string): void;
(e: 'toggle:theme'): void;
}>();
const { t } = useI18n({ useScope: 'global' });
const isDark = useDark({
valueDark: 'dark',
valueLight: 'light'
});
const otherTheme = computed(() => {
return {
displayText: isDark.value
? t('settingsMenu.button.Display.dark')
: t('settingsMenu.button.Display.light')
};
});
const standardButtons = computed(() => {
const buttonsArray = [];
if (props.themeOptions?.toggleEnabled) {
buttonsArray.push({
key: 'themeToggle',
displayText: isDark.value
? t('settingsMenu.button.Display.light')
: t('settingsMenu.button.Display.dark'),
icon: isDark.value ? ['light-mode'] : ['dark-mode'],
function: () => {
if (typeof props.themeOptions.function === 'function') props.themeOptions.function();
isDark.value = !isDark.value;
emit('toggle:theme');
}
});
}
buttonsArray.push({
key: 'settingsToggle',
icon: ['more-vertical'],
neverHide: true
});
return buttonsArray;
});
const libSettingsExpanded = ref(true);
const scrollOverride = ref(false);
const settingsExpanded = computed(() => {
return libSettingsExpanded.value;
});
const settingsButtonMenuItems = ref({
currentButton: '',
menuItems: []
});
const subMenuItems = computed(() => {
const settingsButtonMenuData = settingsButtonMenuItems.value;
return {
currentButton: settingsButtonMenuData.currentButton,
menuItems: settingsButtonMenuData.menuItems
};
});
const showSettingsButtonMenu = computed(() => {
const settingsButtonMenuData = settingsButtonMenuItems.value;
return settingsButtonMenuData.menuItems.length > 0 && settingsExpanded.value;
});
function clickHandler(button) {
const settingsButtonMenuData = settingsButtonMenuItems.value;
settingsButtonMenuItems.value = {
currentButton: '',
menuItems: []
};
if (button?.menuItems) {
if (settingsButtonMenuData.menuItems !== toRef(button.menuItems).value) {
settingsButtonMenuItems.value = {
currentButton: button.displayText,
menuItems: button.menuItems
};
}
} else if (button?.function) {
button.function();
} else if (button?.emitKey) {
emit('button-pressed', button.emitKey);
} else if (button?.key === 'settingsToggle') {
libSettingsExpanded.value = !libSettingsExpanded.value;
}
}
onMounted(() => {
const onScrollStop = callback => {
let isScrolling;
window.addEventListener('scroll', () => {
clearTimeout(isScrolling);
isScrolling = setTimeout(() => {
callback();
}, 100);
});
};
onScrollStop(() => {
if (window.scrollY === 0) {
scrollOverride.value = !scrollOverride.value;
libSettingsExpanded.value = !scrollOverride.value;
} else {
scrollOverride.value = !scrollOverride.value;
libSettingsExpanded.value = !libSettingsExpanded.value;
}
});
});
</script>
<template>
<div
class="e-settings"
role="none"
>
<div
class="e-settings-menu-container"
:class="{
closed: !settingsExpanded
}"
role="menubar"
:aria-expanded="settingsExpanded"
>
<transition-group name="settings-transition">
<template
v-for="settingsButton in menuItems"
:key="settingsButton.key"
>
<div
v-show="settingsExpanded || settingsButton?.neverHide"
class="settings-menu-item"
role="menuitem"
>
<e-settings-button
:active="subMenuItems.currentButton === settingsButton.displayText"
:menu-item="settingsButton"
@click="clickHandler(settingsButton)"
/>
</div>
</template>
<template
v-for="standardSettingsButton in standardButtons"
:key="standardSettingsButton.key"
>
<div
v-show="settingsExpanded || standardSettingsButton?.neverHide"
class="settings-menu-item"
:class="{
'theme-switcher': standardSettingsButton.key === 'themeToggle'
}"
role="menuitem"
>
<e-settings-button
:active="subMenuItems.currentButton === otherTheme.displayText"
:menu-item="standardSettingsButton"
@click="clickHandler(standardSettingsButton)"
/>
</div>
</template>
</transition-group>
</div>
<e-settings-button-menu
v-show="showSettingsButtonMenu"
:aria-expanded="showSettingsButtonMenu"
:parent-button="subMenuItems.currentButton"
:menu-items="subMenuItems.menuItems"
@button-pressed="emitKey => clickHandler({ emitKey: emitKey })"
/>
</div>
</template>
<style scoped lang="scss">
.e-settings {
height: 100px;
position: absolute;
right: 15px;
z-index: 3000;
border-bottom-left-radius: 15px;
overflow: visible;
&.mobile-device {
right: 0;
}
.e-settings-button-menu {
position: fixed;
height: calc(100% - 100px);
bottom: 0;
left: 0;
}
.e-settings-menu-container {
background-color: var(--e-secondary);
display: flex;
flex-direction: row;
align-items: center;
justify-items: center;
gap: 15px;
height: 100px;
position: absolute;
right: 0;
border-bottom-left-radius: 15px;
.settings-transition {
.settings-transition-enter-active,
.settings-transition-leave-active {
transition: all 0.3s ease-in-out;
}
.settings-transition-enter-from,
.settings-transition-leave-to {
opacity: 0;
transform: translateX(20px);
}
}
> :first-child {
&.settings-menu-item {
padding-left: 15px;
}
}
}
}
@media only screen and (min-width: 1000px) {
.e-settings {
width: auto;
.e-settings-button-menu {
position: relative;
height: fit-content;
margin-top: 100px;
padding-right: 25px;
}
.e-settings-menu-container {
gap: 25px;
margin-right: 0;
column-gap: 25px;
}
}
}
</style>