import {ExampleCodeBlock, SymbolDoc} from '@workday/canvas-kit-docs';

import Basic from './examples/Basic';
import StringChildren from './examples/StringChildren';
import DynamicItemsStrings from './examples/DynamicItemsStrings';
import DynamicItemsObjects from './examples/DynamicItemsObjects';
import Virtualization from './examples/Virtualization';
import DataLoader from './examples/DataLoader';
import BasicVirtual from './examples/BasicVirtual';
import IdentifiedItems from './examples/IdentifiedItems';
import RovingFocus from './examples/RovingFocus';
import Selection from './examples/Selection';
import MultiSelection from './examples/MultiSelection';
import BasicGrid from './examples/BasicGrid';
import WrappingGrid from './examples/WrappingGrid';
import OverflowVerticalList from './examples/OverflowVerticalList';


# Canvas Kit Collection API

The Collection API is a system of models and behaviors for creating lists and grids. For example,
`Tabs` uses collection behaviors and so does `Menu`. The UI of each looks very different, but much
of the behavior is shared. The Collection API should be used if a component doesn't already exist to
satisfy your needs.

## Installation

```sh
yarn add @workday/canvas-kit-react
```

## Usage

### Basic Example

The `ListBox` on its own isn't very useful. It registers each item with the model. The
`ListBox.Item` only uses the `useListItemRegister` hook which handles registration of static items
to the model. The `ListBox` uses `useListRenderItems` which handles rendering static items as well
as [dynamic items](#dynamic-items).

<ExampleCodeBlock code={Basic} />

### Identifying Items

A list item takes an optional `data-id` property that will be used to identify an item. Without a
`data-id`, the identifier will be the item's index when first registered. The basic example has a
`data-id` attribute that is a string representation of the index. Providing a `data-id` will
override to a value of your choosing. This identifier will be used by other hooks to identify the
item for selection, maintaining a cursor, or anything else.

<ExampleCodeBlock code={IdentifiedItems} />

### Dynamic Items

The `ListBox` also handles a dynamic collection of items. Instead of providing each `ListBox.Item`
statically, provide a render function instead. The function is called with an `item` value that is
the same as what's provided to the model.

#### Registering Items

Dynamic items can be passed in as an array of strings or objects via the `items` prop or the model
config.

##### Array of Strings

Use strings when your data is a simple list of options. Each string value is used as the unique
identifer.

<ExampleCodeBlock code={DynamicItemsStrings} />

##### Array of Objects

Use objects when your data contains additional information. Each object must have an `id` to ensure
it's registered and searchable. Additional information such as icons or if the item is disabled can
be included in the object and will be accessible in the render function.

<ExampleCodeBlock code={DynamicItemsObjects} />

> **Note: The `id` should match your text value for each item. This ensures proper selection of
> items in components like `Select` and `Autocomplete`. If your `id` is different from your text
> value, you'll have to add a
> [translation layer](https://codesandbox.io/s/combobox-object-items-forked-mc7tm4). **

#### Virtualization

By default, providing items dynamically will enable virtualization. This example adds a `maxHeight`
to ensure overflow. Virtualization uses absolute positioning of each item, which could cause
problems for popup menus. If your item count is low, pass `shouldVirtualize={false}` to disable
virtualization.

<ExampleCodeBlock code={Virtualization} />

### DataLoader

The collection system comes with a data loader to help with dynamic collections. You must provide
`total`, `pageSize`, and an asynchronous `load` function. The loader will call the `load` function
when the user navigates the collection and needs more data. This example shows how to hook up a
simple data loader and mocks a `load` function to simulate an asynchronous response.

The data loader also takes a model argument to know which model to create. The loader configures the
model to handle asynchronous keyboard navigation and will return the configured model to you to pass
along to the collection component.

<ExampleCodeBlock code={DataLoader} />

#### Roving Tabindex

The list system also includes a cursor that extends the list. A cursor is mostly used for focusing
items. The
[roving tabindex](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex)
is a well-supported way to accomplish accessibility requirements for focusing items within a list.
This example shows how to use `useListRovingFocus`. This example uses the `ListBox` component, but
the default `ListBox.Item` is very basic. We have two options, we can either pass additional
functionality via `elemPropsHook` or by creating a new item using our elemProps hook primitives.
Both will be demonstrated. Creating a custom item is recommended if you create a custom component
and export it. Using `elemPropsHook` with `ListBox.Item` is recommended only for one-off instances.

You can either use the tab key for focus on an item or click on an item and then use the up/down
keys to navigation the list. By default, the list is set to wrap navigation using the
`wrappingNavigationManager`. Only a single item in the list is a focus stop that "roves" as the
up/down arrows are pressed.

**Note:** This example doesn't meet accessibility requirements. The list will have to have some type
of context. Like "navigation list" or "menu list".

<ExampleCodeBlock code={RovingFocus} />

#### Selection

Lists support selection. `useSelectionItem` is applied to an item which adds an `onClick` that adds
the item to the `state.selectedIds`. The default selection manager is a single select. This example
uses `ListBox` and creates a custom `SelectableItem` elemProps hook and component.

<ExampleCodeBlock code={Selection} />

### String Children

Sometimes it is desired to allow the string children to be the identifiers for each item. This could
be useful for autocomplete components where the item's text is the desired identifier. Normally, if
no `data-id` is provided to the item, the system will choose the registration index. This would be
`'0'`, `'1'`, and so on. By passing `useListItemAllowChildStrings` to an item component, it will
change this behavior to use the child text if no `data-id` is provided.

<ExampleCodeBlock code={StringChildren} />

#### Multiple Selection

The `selection` manager can be passed directly to the model configuration to handle different
selection types. This example passes the `multiSelectionManager` to handle selecting multiple items.

<ExampleCodeBlock code={MultiSelection} />

### Basic Grid

A grid is a two dimensional list. A `columnCount` config is added to inform how to break up an array
of items. A grid is very similar to a list - it receives items as a single dimension list and uses
the `columnCount` to determine keyboard navigation. Grids only support a single orientation.

<ExampleCodeBlock code={BasicGrid} />

#### Wrapping Grid

By default, navigating a grid does not wrap around when the user reaches the end of a row or column.
The grid model supports passing in a navigation manager. The collection system supports two types of
navigation managers, a non-wrapping `navigationManager` and the `wrappingNavigationManager`. The
following example passes the `wrappingNavigationManager` manager to the model. Observe how the
cursor wraps around columns and rows when an edge of a column or row is encountered.

<ExampleCodeBlock code={WrappingGrid} />

### Overflow Vertical List

A List can overflow vertically or horizontally to account for responsive resizing or an overflow of
items. Using multiple hooks from the Collection system like `useOverflowListModel` and ensuring that
`orientation`is set to`vertical`, you can achieve vertical overflow lists. In the example below,
when the window is resized vertically, items in the Sidebar will overflow into the "More Actions"
button.

<ExampleCodeBlock code={OverflowVerticalList} />

## Component API

### ListBox

<SymbolDoc name="ListBox" fileName="/react/" />
<SymbolDoc name="useGridModel" fileName="/react/" />

### Navigation Manager

<SymbolDoc name="NavigationManager" fileName="/react/" />

#### `navigationManager`

The `navigationManager` implements the [Navigation Manager](#navigation-manager) interface for lists
and grids, but does not wrap when the user hits a boundary of the collection. This is the default
navigation manager for grids.

#### `wrappingNavigationManager`

The `wrappingNavigationManager` implements the [Navigation Manager](#navigation-manager) interface
for lists and grids, and wraps when the user hits a boundary of the collection. Grids will wrap
columns, but not rows. This is the default navigation manager for lists.

### Selection Manager

<SymbolDoc name="SelectionManager" />

## Hooks

<SymbolDoc name="useListItemRegister" fileName="/react/" />
<SymbolDoc name="useListItemAllowChildStrings" fileName="/react/" />
<SymbolDoc name="useListItemRovingFocus" fileName="/react/" />
<SymbolDoc name="useListItemSelect" fileName="/react/" />
<SymbolDoc name="useListRenderItems" fileName="/react/" />
<SymbolDoc name="useListResetCursorOnBlur" fileName="/react/" />
<SymbolDoc name="useOverflowListItemMeasure" fileName="/react/" />
<SymbolDoc name="useOverflowListMeasure" fileName="/react/" />
<SymbolDoc name="useOverflowListTarget" fileName="/react/" />
<SymbolDoc name="useListLoader" fileName="/react/" />