import { useRef, useCallback, useState, useMemo } from 'react'
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs'
import useGridKeyboardNavigation from '../useGridKeyboardNavigation'
import { action } from '@storybook/addon-actions'
import cx from 'classnames'
import { range } from 'lodash-es'
import './useGridKeyboardNavigation.stories.scss'
import Button from '../../../../src/components/Button/Button'

<Meta title='Hooks/useGridKeyboardNavigation' />

# useGridKeyboardNavigation

- [Overview](#overview)
- [Usage](#usage)
- [Arguments](#arguments)
- [Returns](#returns)
- [Feedback](#feedback)

## Overview

Used for accessible keyboard navigation. Useful for components rendering items that can be navigated and selected with a keyboard.

export const ELEMENT_WIDTH_PX = 72
export const PADDING_PX = 24

export const ON_CLICK = action('item selected')

<Canvas>
  <Story name='Overview'>
    {() => {
      const ref = useRef(null)
      const disabledIndexes = [2, 4, 6]
      const [itemsCount, setItemsCount] = useState(15)
      const [numberOfItemsInLine, setNumberOfItemsInLine] = useState(4)
      const width = useMemo(
        () => numberOfItemsInLine * ELEMENT_WIDTH_PX + 2 * PADDING_PX,
        [numberOfItemsInLine],
      )
      const items = useMemo(() => range(itemsCount).map(num => `${num}.`), [itemsCount])
      const getItemByIndex = useCallback(index => items[index], [items])
      const { activeIndex, onSelectionAction } = useGridKeyboardNavigation({
        ref,
        numberOfItemsInLine,
        itemsCount,
        getItemByIndex,
        onItemClicked: ON_CLICK,
        disabledIndexes,
      })
      const onClickByIndex = useCallback(
        index => () => onSelectionAction(index),
        [onSelectionAction],
      )
      return (
        <div>
          <div
            className='use-grid-keyboard-nav-comp-wrapper'
            style={{ width }}
            ref={ref}
            tabIndex={-1}
          >
            {items.map((item, index) => (
              <Button
                key={item}
                disabled={disabledIndexes.includes(index)}
                onClick={onClickByIndex(index)}
                kind={Button.kinds?.SECONDARY}
                className={cx('use-grid-keyboard-nav-item', {
                  'active-item': index === activeIndex,
                })}
              >
                {item}
              </Button>
            ))}
          </div>
          <div className='use-grid-keyboard-nav-controls'>
            <div>
              Items count:{' '}
              <input
                value={itemsCount}
                onChange={e => setItemsCount(e.target.value)}
                type='number'
                min={1}
              />
            </div>
            <div>
              Number of items in line:{' '}
              <input
                value={numberOfItemsInLine}
                onChange={e => setNumberOfItemsInLine(e.target.value)}
                type='number'
                min={1}
              />
            </div>
          </div>
        </div>
      )
    }}
  </Story>
</Canvas>

## Usage

<UsageGuidelines
  guidelines={['Use this hook when you want to add keyboard navigation to a grid-like component.']}
/>

## Arguments

<FunctionArguments>
  <FunctionArgument name='options' type='Object'>
    <FunctionArgument
      name='ref'
      type='React.MutableRefObject<HTMLElement>'
      description={
        <>
          A React{' '}
          <a
            href='https://reactjs.org/docs/hooks-reference.html#useref'
            target='_blank'
            rel='noopener noreferrer'
          >
            ref
          </a>{' '}
          object. The reference for the component that listens to keyboard. <br />
          <b>Important:</b> the referred element must have a <code>tabIndex={-1}</code> for the focus
          to work properly.
        </>
      }
      required
    />
    <FunctionArgument name='itemsCount' type='Number' description='The number of items.' required />
    <FunctionArgument
      name='numberOfItemsInLine'
      type='Number'
      description='The number of items on each line of the grid.'
      required
    />
    <FunctionArgument
      name='onItemClicked'
      type='(item, index) => void'
      description="The callback for selecting an item. It will be called when an active item is selected, for example with 'Enter'."
      required
    />
    <FunctionArgument
      name='getItemByIndex'
      type='(index) => item'
      description='A function which gets an index as a param, and returns the item on that index.'
    />
    <FunctionArgument
      name='focusOnMount'
      type='boolean'
      description='If true, the referenced element will be focused when mounted.'
    />
    <FunctionArgument
      name='focusItemIndexOnMount'
      type='number'
      description={
        <>
          Optional item index to focus when mounted. Only works with{' '}
          <code>options.focusOnMount</code>.
        </>
      }
    />
    <FunctionArgument
      name='disabledIndexes'
      type='number[]'
      description='Optional array of disabled indices, which will be skipped while navigating.'
    />
  </FunctionArgument>
</FunctionArguments>

## Returns

<FunctionArguments>
  <FunctionArgument name='result' type='Object'>
    <FunctionArgument
      name='activeIndex'
      type='number'
      description='The index of the currently active item.'
    />
    <FunctionArgument
      name='onSelectionAction'
      type='(itemIndex) => void'
      description={
        <>
          A wrapper around the passed <code>onItemClicked</code> function. Use it as the handler for
          selecting items (e.g. <code>onClick</code>)
        </>
      }
    />
    <FunctionArgument
      name='isInitialActiveState'
      type='boolean'
      description={
        <>
          If true, the currently active element was due to an initial mounting index option. See{' '}
          <code>options.focusItemIndexOnMount</code>.
        </>
      }
    />
  </FunctionArgument>
</FunctionArguments>
