Interkit docs

BottomMenu

The BottomMenu is often used as the main navigation for apps. Research has found that most users use their phones with one hand. When they hold their phone, they’ll use either their right or left thumb to interact with the screen. The thumb is like the user’s mouse but with limitations. The bottom is the easiest to reach. For a bottom menu you should place high priority options at the bottom. This makes them quicker to reach and tap.

Example

Here’s the BottomMenu in the context of an interkit app:

Example

How to Build

Use the BottomMenu component together with BottomMenuPage and BottomMenuButton to construct a row of buttons on the bottom that select different screens.

Arrange the components in the following way. This is an example with two buttons and two pages.

  • BottomMenu
    • pages
      • BottomMenuPage
        • TopNavBarCustom
      • BottomMenuPage
        • TopNavBarCustom
    • buttons
      • BottomMenuButton
        • defaultIcon: Icon
        • selectedIcon: Icon
      • BottomMenuButton
        • defaultIcon: Icon
        • selectedIcon: Icon

Pages Buttons

Important Tips

  • the key prop on BottomMenuButton needs to be set to a unique key for each button for it to work
  • currently only works correctly if you put TopNavBarCustom in the BottomMenuPage

Props

Prop Type Default Description
TABS {} {}

Slots

Name Default Props Fallback
buttons No
media_player No
pages No

Source

<script context="module">
  export const TABS = {};
</script>

<script>

  import { setContext, onDestroy } from 'svelte';
  import { writable } from 'svelte/store';
  import { InterkitClient } from '../'

  const tabs = [];
  const panels = [];
  const selectedTab = writable(null);
  const selectedPanel = writable(null);

  let containerHeightPx = 0
  let pagesHeightPx = 0
  let buttonsHeightPx = 0
  let audioPlayerHeightPx = 0

  setContext(TABS, {
      registerTab: (tab, key) => {
        tabs.push(tab);
        
        // set current to the first tab that is registered
        selectedTab.update(current => current || tab);

        // set the UiKey to the first key that is registered
        if(!InterkitClient.getUiKey("bottomMenuKey")) {
          InterkitClient.setUiKey("bottomMenuKey", key);    
        }
        
        onDestroy(() => {
          const i = tabs.indexOf(tab);
          tabs.splice(i, 1);
          selectedTab.update(current => current === tab ? (tabs[i] || tabs[tabs.length - 1]) : current);
        });
      },

      registerPanel: panel => {
        panels.push(panel);
        selectedPanel.update(current => current || panel);
        
        onDestroy(() => {
          const i = panels.indexOf(panel);
          panels.splice(i, 1);
          selectedPanel.update(current => current === panel ? (panels[i] || panels[panels.length - 1]) : current);
        });
      },

      selectTab: (tab) => {
        const i = tabs.indexOf(tab);
        //console.log(i)
        
        selectedTab.set(tab);
        selectedPanel.set(panels[i]);
      },

      selectPanel: (panel) => {
        const i = panels.indexOf(panel);  
        selectedPanel.set(panel)
        selectedTab.set(tabs[i])      
      },

      selectedTab,
      selectedPanel,
      pagesHeightPx
    });

    const audioPlayerStatus = InterkitClient.getGlobalStore("audioPlayerStatus")

    //const throttledMaxHeightUpdate = throttle(100, false, val => $audioPlayerStatus.maxHeightPx = val, true)

    $: if ($audioPlayerStatus) {
      //throttledMaxHeightUpdate(pagesHeightPx + audioPlayerHeightPx)
      $audioPlayerStatus.maxHeightPx = containerHeightPx - buttonsHeightPx
    }

</script>

<div 
  class="BottomMenu container" 
  class:mediaPlayerActive={$audioPlayerStatus?.active}
  bind:clientHeight={containerHeightPx}
  >
  <div class="BottomMenu__Pages pages" bind:clientHeight={pagesHeightPx}>
    <slot name="pages"></slot>
  </div>

  <div class="BottomMenu__MediaPlayer media-player" 
    class:active={$audioPlayerStatus?.active}
    class:expanded={$audioPlayerStatus?.expanded}
    bind:clientHeight={audioPlayerHeightPx}
  >
      <slot name="media_player"></slot>
  </div>

  <div class="BottomMenu__Buttons buttons" bind:clientHeight={buttonsHeightPx}>
    <slot name="buttons"></slot>
  </div>
</div>

<style>

  .container {
    height: 100%;
    display: flex;
    flex-direction: column;
    background-color: var(--background-color);
    /*--bottom-menu-height: 55px;*/
  }

  .pages {
    flex: 1;
    overflow: hidden;
    position: relative;
  }

  .media-player {
    position: relative;
    width: 100%;
    pointer-events: none;
    touch-action: none;
    display: flex;
    overflow: hidden;
  }
  :global(.media-player > *) {
    pointer-events: all;
    touch-action: auto;
  }

  .media-player.active {
    
    /*height: 55px;*/
  }

  .media-player.expanded {
    /*height: 100%;*/
  }

  .buttons {
    width: 100%;
    display: flex;
    height: 64px;
  }


</style>