Skip to content

Groups ​

EPGGroup is a navigation container registered with v-epg-group. Use groups for regions with independent entries or boundaries, such as menus, lists, dialogs and tabs.

vue
<ul v-epg-group>
  <li v-epg-item>Home</li>
  <li v-epg-item>Movies</li>
</ul>

What a group does ​

  1. Search inside first: search the current group; if no target exists, treat the whole group as an origin at the next level.
  2. Default entry: entering a group selects its default entry instead of the geometrically closest item.
  3. Entry and exit events: emit epg-enter / epg-leave; when no target exists in a direction, emit a boundary event before searching outward.

A scroll container can share the group element or be independent. See Scrolling.

Groups may nest at any depth, including ordinary elements or components between them. Membership is determined by the closest registered ancestor in the live DOM on each navigation operation.

Binding options ​

ts
/** `v-epg-group` 的绑定值 */
export interface EPGGroupOptions {
  /** 所在层级被进入时,优先进入该组 */
  default?: boolean;
  /** 禁用:导航时整组被跳过 */
  disabled?: boolean;
}
OptionBehavior
defaultPreferred group when its containing level is entered
disabledSkip the whole group in navigation; its items may still be focused directly with move()

Create groups when you need separate entries or boundary events. A regular grid usually needs one group, not a group per row or card. Crossing groups enters the selected group's default, unlike geometric selection inside a group.

Entering a group uses its defaultLeft · no defaultRight · B2 is default ★A1A2B1B2 ★Choose a group to see its entry · Current focus: None
Choose a group to see its entry, then an item to compare direct movement.
vue
<aside v-epg-group>
  <button v-for="entry in menu" :key="entry.id" v-epg-item>{{ entry.title }}</button>
</aside>

<main v-epg-group>
  <div v-epg-scroll class="card-list">
    <button v-for="movie in movies" :key="movie.id" v-epg-item>{{ movie.title }}</button>
  </div>
</main>

Which item receives focus on entry ​

Try these in order:

  1. Children marked default.
  2. Other children in document order.
  3. If a child is a group, enter recursively using the same rule.

An empty group, or one with no available items, cannot become a direction target.

To return to the last selected item when entering again, point a reactive default at its ID. If the item is removed, entry falls back to another available item:

vue
<script setup lang="ts">
import { ref } from "vue";

const lastId = ref<string | null>(null);
</script>

<template>
  <div v-epg-group>
    <button
      v-for="movie in movies"
      :key="movie.id"
      v-epg-item="{ default: lastId === movie.id }"
      @epg-focus="lastId = movie.id"
    >
      {{ movie.title }}
    </button>
  </div>
</template>

Events ​

EventWhenCancels default navigation
epg-enterFocus enters the groupNo
epg-leaveFocus leaves the groupNo
Direction eventsNo target exists in that direction inside the groupYes
vue
<!-- Collapse the sidebar when focus leaves it -->
<aside v-epg-group @epg-enter="expand = true" @epg-leave="expand = false">...</aside>

<!-- Dialog: block focus from leaving in any direction -->
<div v-epg-group @epg-up.prevent @epg-down.prevent @epg-left.prevent @epg-right.prevent>...</div>

See Events for the full event contract.