SHIFT + D

What is Baleada Features?

Updated on August 26, 2024Source code

Baleada Features is a collection of composables that implement all kinds of useful features in Vue 3:

  • Fine-grained press interactions, normalized across browsers, devices, and input types (mouse, touch, keyboard, etc.)
  • Focus trapping
  • Excel-inspired keyboard interactions for data grids, tables, single- and multi-select dropdowns, menus, and more
  • VS-Code-inspired undo/redo functionality for text inputs
  • Fuzzy matching for typeahead search on data grids, tables, select dropdowns, menus, and more
  • ARIA role & attribute management for complex, fully accessible custom components
  • First-class support for accessible labelling
  • Reactive tracking for various DOM element states (focus, hover, transition, size, intersection, etc.)
  • So, so much more

Baleada Features doesn't include any markup or styles. It only ships composables, which you can use to power your own custom component library, with your own custom markup and styles.

Installation

Right now, Baleada Features is only implemented in Vue 3:

npm i @baleada/vue-features

Using functions

The functions exported by Baleada Features fall into five categories:

  1. Interfaces
  2. Combos
  3. Extensions
  4. Affordances
  5. Transforms

Interfaces are composables that add rich interactivity and accessibility to your markup.

Interfaces include:

Jump to the Using interfaces section to learn more about basic interface usage.

Combos are composables that combine multiple interfaces under the hood. Combos include:

Jump to the Using combos section to learn more about basic combo usage.

Extensions are composables that track various DOM element states and/or add unique features to interfaces, combos, or plain HTML elements.

Extensions include:

Jump to the Using extensions section to learn more about basic extension usage.

Affordances are functions that implement certain features that are readily available in Vue templates, but are not so easy to figure out inside of composables.

Affordances include:

Affordances are primarily used internally by interfaces and extensions. However, they're exported publicly for authors who want to write their own composables, following the style and patterns of Baleada Features, and organizing code by logical concern.

Jump to the Using affordances section to learn more about basic affordance usage.

Transforms are small utility functions for working with function refs, the awesome Vue feature that is the backbone of Baleada Features. For more info on function refs and how they're used in Baleada Features, see the Element API guide.

Transforms include:

Visit any transform's dedicated guide to learn more about its use cases and usage.

Using interfaces

Import any interface to start using it in your Vue component. Most often, you'll use interfaces in script setup:

<script setup>
import { useTablist } from '@baleada/vue-features'

const tablist = useTablist(...)
</script>

Every interface accepts only one parameter: an options object that customizes basic functionality. Every interface returns an object with tools you can use to set up and control your UI.

<script setup>
import { useTextbox } from '@baleada/vue-features'

// For example, use the `options` object to customize
// the initial value of a textbox:
const textbox = useTextbox({ initialValue: 'Hello world' })

function handleUndoButtonClick () {
  // Use the `undo` method of the returned `textbox`
  // object to programmatically undo changes:
  textbox.undo()
}
</script>

To learn more, visit the docs for each interface exported by Baleada Features. For a complete list of available interfaces, see the Interfaces section under the Features heading in the left sidebar.

Using combos

Import any combo to start using it in your Vue component. Most often, you'll use combos in script setup:

<script setup>
import { useSelect } from '@baleada/vue-features'

const select = useSelect(...)
</script>

Every combo accepts only one parameter: an options object that customizes basic functionality. Every combo returns an object with tools you can use to set up and control your UI.

<script setup>
import { useSelect } from '@baleada/vue-features'

// For example, use the `options` object to customize
// the initially selected value of the select's listbox
// ("select" is a combination of a listbox and a button):
const select = useSelect({
  listbox: { multiselectable: true, initialSelected: [3, 6] }
})
</script>

To learn more, visit the docs for each combo exported by Baleada Features. For a complete list of available combos, see the Combos section under the Features heading in the left sidebar.

Using extensions

Import any extension to start using it in your Vue component. Most often, you'll use extensions in script setup:

<script setup>
import { useTextbox, useTextboxStorage } from '@baleada/vue-features'

// Set up a textbox interface
const textbox = useTextbox()

// To extend your `textbox`'s functionality, pass it into the
// `useTextboxStorage` extension.
//
// Now, the textbox's value will automatically get stored in 
// `localStorage`, and when the component is mounted, your 
// `textbox` will update its value if `localStorage` has 
// any contents.
const storage = useTextboxStorage(textbox, { key: 'my textbox' })
</script>

As their first parameter, extensions can accept one of three things:

  • The return value of a Baleada Features interface function
  • OR an interface included in the return value of a combo function (for example, you can pass useSelect().listbox to an extension)
  • OR a reactive reference to an HTML element

Not every extension accepts all of these things, though. useMarkdownCompletion, for example, specifically accepts the textbox object returned from the useTextbox interface, and won't accept any other interface objects, nor a reactive reference to an HTML element.

The docs for each extension, along with TypeScript types, will let you know what the extension accepts as valid first parameters.

useSize, for example, can accept any of those three options:

<script setup>
import { ref, onMounted } from 'vue'
import { useSize } from '@baleada/vue-features'

const body = ref()
onMounted(() = body.value = document.body)

// We could pass an interface object as the first argument,
// but in this example, we'll pass a reactive reference to
// the document body:
const size = useSize(body)
</script>

As the second parameter, many extensions also accept an options object that customizes functionality.

Every extension returns an object with tools you can use to set up and control your extended functionality.

<script setup>
import { ref, readonly, watchEffect } from 'vue'
import { useSize } from '@baleada/vue-features'

const body = ref()
onMounted(() = body.value = document.body)

// Optionally use `readonly` to unwrap all refs, so
// everything can be accessed reactively without using
// `.value`:
const size = readonly(useSize(body))

const addOrientationClass = () => {
  document.body.classList.add(`orientation-${size.orientation}`)
}

const cleanupOrientationClasses = () => {
  document.body.classList.remove(
    'orientation-landscape',
    'orientation-portrait',
    'orientation-none'
  )
}

watchEffect(() => {
  // Clean up any previous effects
  cleanupOrientationClasses()
  
  // Reactively add a class to the body based on its orientation:
  addOrientationClass()
})
</script>

To learn more, visit the docs for each extension exported by Baleada Features. For a complete list of available extensions, see the Extensions section under the Features heading in the left sidebar.

Using affordances

Baleada Features' Vue implementation also exports "affordances": functions that re-implement certain features that are readily available in Vue templates, but are not so easy to figure out inside composables.

import { bind, on, show, model } from '@baleada/vue-features'

Vue templates, for example, allow you to easily bind static reactive data to the attributes of any element:

<!-- MyComponent.vue -->
<template>
  <div
    role="tab"
    :aria-selected="isSelected"
    :class="isSelected ? 'border-blue-600' : 'border-gray-200'"
  >
    ...
  </div>
</template>

Inside a composable, it's not obvious how to properly recreate this behavior, but Baleada Features' bind affordance makes it a cinch:

// Inside script setup
import { bind } from '@baleada/vue-features'

const element = ref<HTMLElement>(null),
      isSelected = computed(...)

bind(
  element,
  {
    // bind can assign static values
    role: 'tab',
    // But it really shines with reactive values
    ariaSelected: isSelected,
    class: computed(() => 
      isSelected.value 
        ? 'border-blue-600' 
        : 'border-gray-200'
    ),
  }
)

Affordances are primarily used internally by interfaces and extensions. However, they're exported publicly for authors who want to write their own composables, following the style and patterns of Baleada Features, and organizing code by logical concern

To learn more, visit the docs for each affordance exported by Baleada Features. For a complete list of available affordances, see the Affordances section under the Features heading in the left sidebar.

What is Baleada Edge?Interfaces

Edit doc on GitHub

ON THIS PAGE

What is Baleada Features?InstallationUsing functionsUsing interfacesUsing combosUsing extensionsUsing affordances