1.0.41 • Published 5 months ago

navkit-vue v1.0.41

Weekly downloads
-
License
ISC
Repository
github
Last release
5 months ago

NavKit

NavKit is a lightweight, flexible Vue.js navigation library designed for keyboard and TV remote control navigation. It provides an intuitive way to handle spatial navigation in your applications, perfect for smart TV apps, set-top boxes, and keyboard-focused web applications.

Features

  • 🎯 Precise spatial navigation with arrow keys
  • 📺 Perfect for TV applications and remote controls
  • ⌨️ Full keyboard support
  • 🔄 Cyclic navigation option
  • 🔀 Customizable navigation patterns
  • 🎮 Game-like navigation control
  • ↔️ Single-axis navigation support (horizontal or vertical only)
  • ⚡ Lightweight and performant
  • 🔌 Easy to integrate
  • 🎨 Customizable focus styling

Installation

npm install navkit-vue

Basic Usage

The library provides three navigation hooks:

  • useNavigation: Full 2D grid navigation
  • useNavigationX: Horizontal-only navigation
  • useNavigationY: Vertical-only navigation

2D Navigation

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

  const rows = [3, 3, 3]; // Grid with 3 rows, 3 items each

  const { currentElement, currentRow } = useNavigation({
    rows,
    focusableSelector: "[data-keyboard]", // Custom key - default is [data-focusable]
    onEnter: () => {
      console.log("Enter pressed on:", currentElement.value); //Callback for each keyboard event
    },
  });
</script>

<template>
  <div class="grid">
    <button v-for="item in items" :key="item.id" data-keyboard>
      {{ item.label }}
    </button>
  </div>
</template>

Single-Axis Navigation

For simpler navigation patterns, you can use the dedicated horizontal or vertical navigation hooks:

// Horizontal-only navigation
import { useNavigationX } from "navkit-vue";

const { currentElement } = useNavigationX({
  columns: 5, // Number of items
  focusableSelector: "[data-keyboard]",
});

// Vertical-only navigation
import { useNavigationY } from "navkit-vue";

const { currentElement } = useNavigationY({
  rows: 3, // Number of items
  focusableSelector: "[data-keyboard]",
  cyclic: true,
});

API Reference

useNavigation Options

OptionTypeDefaultDescription
rowsRef<number[]>RequiredArray defining the number of items in each row
initialPositionPositionType{ row: 0, col: 0 }Starting position
disabledRef<boolean>falseDisables navigation when true
focusableSelectorstring'[data-focusable]'CSS selector for focusable elements
autofocusbooleantrueAutomatically focus first element on mount
focusClassstring'focused'CSS class applied to focused element
cyclicbooleanfalseEnable wrapping around edges
invertAxisbooleanfalseSwap vertical/horizontal navigation
autoNextRowbooleanfalseAuto-advance to next row
holdColumnPerRowbooleanfalseMaintain column position when changing rows

useNavigationX / Y Options

OptionTypeDefaultDescription
columns/rowsRef<number>RequiredNumber of navigable items
initialPositionnumber0Starting position
disabledRef<boolean>falseDisables navigation when true
focusableSelectorstring'[data-focusable]'CSS selector for focusable elements
autofocusbooleantrueAutomatically focus first element on mount
focusClassstring'focused'CSS class applied to focused element
cyclicbooleanfalseEnable wrapping around edges
onRowStartCallback FnnullCallback Fn on Row Start
onRowEndCallback FnnullCallback Fn on Row End
onEnterCallback FnnullCallback Fn on Enter
onReturnCallback FnnullCallback Fn on Back

Callback Events

EventParametersDescription
onColumnStart() => voidCalled when navigation reaches first column
onColumnEnd() => voidCalled when navigation reaches last column
onRowStart() => voidCalled when navigation reaches first row
onRowEnd() => voidCalled when navigation reaches last row
onEnter(position: PositionType) => voidCalled when Enter key is pressed
onReturn(position: PositionType) => voidCalled when Return/Back key is pressed
onDown() => voidFor X Navigation Type
onUp() => voidFor X Navigation Type
onLeft() => voidFor Y Navigation Type
onRight() => voidFor Y Navigation Type

Return Values

ValueTypeDescription
positionRef<PositionType>Current focus position
currentRownumberCurrent row index
currentElementRef<HTMLElement \| null>Currently focused element
isDisabledRef<boolean>Current disabled state
setRowPosition(row: number, position: number) => voidSet position for specific row
toggleDisabled(value?: boolean) => voidToggle or set disabled state
isValidPosition(position: PositionType) => booleanCheck if position is valid
focusElement(element: HTMLElement \| null) => voidProgrammatically focus element

Styling

NavKit uses a class-based approach for styling focused elements. By default, it applies the focused class to the currently focused element. You can customize this by:

.focused {
  outline: 2px solid #007bff;
  box-shadow: 0 0 10px rgba(0, 123, 255, 0.5);
}

Advanced Usage

Custom Grid Layout

<script setup lang="ts">
  const rows = computed(() => [
    firstRow.value.length,
    secondRow.value.length,
    thirdRow.value.length,
  ]); // Irregular grid layout

  const { currentElement } = useNavigation({
    rows,
    cyclic: true, // Enable wrap-around navigation
    autoNextRow: true, // Auto-advance to next row
    focusableSelector: "[data-selector]", // This is optinal, default is `[data-focusable]`
    onEnter: (position) => {
      console.log("Selected position:", position);
    },
  });
</script>

Single-Axis Navigation Example

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

  const columns = computed(() => columns.value.length);

  const { currentElement } = useNavigationX({
    columns,
    cyclic: true,
    onEnter: () => {
      console.log("Selected:", currentElement.value?.textContent);
    },
  });
</script>

<template>
  <div class="horizontal-menu">
    <button v-for="item in menuItems" :key="item.id" data-keyboard>
      {{ item.label }}
    </button>
  </div>
</template>

TV Remote Navigation

<script setup lang="ts">
  const { currentElement, toggleDisabled } = useNavigation({
    rows: ref([3, 3, 3]),
    focusableSelector: "[data-keyboard]",
    focusClass: "tv-focused",
    onReturn: () => {
      // Handle back button
      router.back();
    },
  });
</script>

Scroll Into Focus

The useScrollIntoFocus hook provides automatic scrolling functionality to ensure focused elements are always visible within their container. This is particularly useful for large lists or grids where content may extend beyond the viewport.

import { useScrollIntoFocus } from "navkit-vue";

const { currentElement, position } = useNavigation({
  rows: [5, 5, 5],
});

useScrollIntoFocus({
  position,
  selectedElement: currentElement,
  behavior: "smooth",
  parentSelector: "[data-parent]",
  buffer: 180,
});

useScrollIntoFocus Options

OptionTypeDefaultDescription
positionRef<PositionType>RequiredCurrent navigation position
selectedElementRef<HTMLElement \| null>RequiredCurrently focused element
behavior"smooth" \| "auto""smooth"Scroll behavior
delaynumber1000Delay in milliseconds for scroll throttling/debouncing
parentSelectorstring"[data-parent]"CSS selector for scrollable container
buffernumber180Default buffer space around focused element (in pixels)
bufferXnumberbufferHorizontal buffer space (overrides default buffer)
bufferYnumberbufferVertical buffer space (overrides default buffer)
suppressLogsbooleantrueSuppress warning logs
scrollType"throttle" \| "debounce""throttle"Scroll event handling type

Browser Support

NavKit supports all modern browsers and smart TV platforms that support Vue.js.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1.0.41

5 months ago

1.0.40

5 months ago

1.0.39

5 months ago

1.0.38

5 months ago

1.0.37

5 months ago

1.0.36

5 months ago

1.0.35

5 months ago

1.0.34

5 months ago

1.0.33

5 months ago

1.0.32

5 months ago

1.0.30

5 months ago

1.0.29

5 months ago

1.0.28

5 months ago

1.0.27

5 months ago

1.0.25

5 months ago

1.0.24

5 months ago

1.0.23

5 months ago

1.0.22

5 months ago

1.0.21

5 months ago

1.0.19

5 months ago

1.0.16

5 months ago

1.0.15

5 months ago

1.0.13

5 months ago

1.0.11

5 months ago

1.0.10

5 months ago

1.0.8

5 months ago

1.0.7

5 months ago

1.0.6

5 months ago

1.0.5

5 months ago

1.0.4

5 months ago

1.0.3

5 months ago

1.0.2

5 months ago

1.0.1

5 months ago

1.0.0

5 months ago