@econnect/webcomponents-library
Documentation
Styleguide
Documentation
Styleguide
  • Getting Started

    • Installation
    • Patch Notes
    • Styleguide
  • Styling

    • e-connect colors
  • UI Components

    • Form

      • Search Field
      • Form Field
      • Forms
    • Filter

      • Filter
      • Search Filter
    • Grid

      • Grid
      • Grid Row
    • Inputs

      • File Input
      • Input Checkbox
      • Input Dropdown
      • Input Date
      • Input Date Time
      • Input Date Time Local
      • Input File
      • Input Label
      • Input Multi Select
      • Input Number
      • Input Radio
      • Input Range
      • Input Select
      • Input Switch
      • Input Slider
      • Input Text
      • Input TextArea
      • Input Time
      • Input Time Range
    • Controls

      • Booleans
      • Button
      • Checkboxes
      • Date Displays
      • Dropdown Lists
      • Text Displays
    • Headers

      • e-header
    • Cards

      • e-card
    • Images

      • Icon
      • Images
      • Flag Image
      • Hamburger
    • Iframe
    • Pager
    • Settings
    • Document Validation
    • Navigation

      • Navigation
      • Navigation Menu
      • Navigation Footer
      • Navigation Button
      • Navigation Button Item
      • Navigation Search Item

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

ValueTypeOptionalDefault
menuItemsEMenuItem[]yes
themeOptionsObjectyes

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>
Last Updated:: 9/1/25, 1:11 PM
Contributors: Antony Elfferich, Marcel Lommers
Prev
Pager
Next
Document Validation