Primitives / foldkit-primitives/interaction

ListNavigation

namespace · packages/primitives/src/interaction/list-navigation.ts:9

export * as ListNavigation from './list-navigation.js'

Keyboard navigation of a list in one placement: a roving tab stop, type to find, and PageUp and PageDown. One Bundle holds the current item's id and the typeahead query, so a printable key can move the pointer and extend the query in one transition, which two placements cannot do under virtual. The resolver allows one owner per event on a slot, so a host that needs both arrows and typeahead takes this, not RovingTabindex plus Typeahead.

In the guides

Members

ListNavigation.Args

const · packages/primitives/src/interaction/list-navigation.ts:36

const Args: Schema.Struct<{
  readonly orientation: Schema.Literals<
    readonly ['vertical', 'horizontal', 'both']
  >
  readonly loop: Schema.Boolean
  readonly virtual: Schema.Boolean
  readonly timeoutMs: Schema.Number
  readonly page: Schema.Number
}>

ListNavigation.behavior

function · packages/primitives/src/interaction/list-navigation.ts:133

function behavior<Field extends string>(
  declared: Declared<typeof bundle, Field>,
  args: Args,
): <Slots>(
  slots: Slots,
) => <Input extends { readonly [K in Field]: Model }, ParentMessage>(
  options: BehaviorOptions<Input, Slots, ParentMessage>,
) => Behavior.NamedBehavior<Slots, Input, ParentMessage>

Wires a placed ListNavigation to the slots. The container gets one key handler: arrows, Home, End, PageUp and PageDown move by id; a printable key extends the query and moves to its match. Each item gets tabindex and OnFocus as under RovingTabindex. Under virtual nothing takes DOM focus and the container carries aria-activedescendant.

ListNavigation.BehaviorOptions

interface · packages/primitives/src/interaction/list-navigation.ts:79

interface BehaviorOptions<Input, Slots, ParentMessage> {
  readonly container: keyof Slots & string
  readonly item: keyof Slots & string
  readonly items: (input: Input) => Behaviors.Collection.Items<unknown>
  readonly text: (input: Input, index: number) => string
  readonly direction?: (input: Input) => RovingTabindex.Direction
  readonly typeahead?: boolean
  readonly commit?: (input: Input, id: string) => ParentMessage
}

ListNavigation.bundle

const · packages/primitives/src/interaction/list-navigation.ts:50

const bundle: Bundle.Bundle<
  'ListNavigation',
  {
    readonly orientation: 'horizontal' | 'vertical' | 'both'
    readonly loop: boolean
    readonly virtual: boolean
    readonly timeoutMs: number
    readonly page: number
  },
  {
    readonly current: string | null
    readonly query: string
    readonly generation: number
  },
  | { readonly _tag: 'Cleared' }
  | { readonly _tag: 'Focused'; readonly id: string }
  | {
      readonly _tag: 'Typed'
      readonly char: string
      readonly match: string | null
    }
  | { readonly _tag: 'Expired'; readonly generation: number },
  never,
  never,
  never,
  void,
  {},
  {}
>

ListNavigation.KeyOutcome

type · packages/primitives/src/interaction/list-navigation.ts:104

type KeyOutcome =
  | {
      readonly _tag: 'Navigate'
      readonly id: string
    }
  | {
      readonly _tag: 'Type'
      readonly char: string
      readonly match: string | null
    }

The key's outcome, before it is turned into an attribute: navigation to an id, a typed character with its match, or nothing.

ListNavigation.keyOutcome

function · packages/primitives/src/interaction/list-navigation.ts:108

function keyOutcome(
  items: Behaviors.Collection.Items<unknown>,
  texts: ReadonlyArray<string>,
  state: {
    readonly current: string | null
    readonly query: string
  },
  key: string,
  modifiers: KeyboardModifiers,
  options: RovingTabindex.MoveOptions,
): KeyOutcome | undefined

ListNavigation.Message

const · packages/primitives/src/interaction/list-navigation.ts:26

const Message: MessageUnion<{
  readonly Focused: { readonly id: Schema.String }
  readonly Typed: {
    readonly char: Schema.String
    readonly match: Schema.NullOr<Schema.String>
  }
  readonly Expired: { readonly generation: Schema.Number }
  readonly Cleared: {}
}>

ListNavigation.Model

const · packages/primitives/src/interaction/list-navigation.ts:19

const Model: Schema.Struct<{
  readonly current: Schema.NullOr<Schema.String>
  readonly query: Schema.String
  readonly generation: Schema.Number
}>