Skip to content

Commit

Permalink
feat(List): new component
Browse files Browse the repository at this point in the history
  • Loading branch information
DarkGhostHunter committed Nov 24, 2023
1 parent 11ccbbb commit 429c852
Show file tree
Hide file tree
Showing 12 changed files with 690 additions and 3 deletions.
39 changes: 39 additions & 0 deletions docs/components/content/examples/ListExampleObjectButtons.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<template>
<UList :items="items" />
</template>

<script setup>
const items = [
{
label: 'Store',
icon: 'i-heroicons-building-storefront',
color: 'blue',
variant: 'solid',
to: '#store'
},
{
label: 'Clients',
icon: 'i-heroicons-user-group',
color: 'cyan',
to: '#clients'
},
{
label: 'Products',
icon: 'i-heroicons-gift',
color: 'green',
to: '#products'
},
{
label: 'Warehouses',
icon: 'i-heroicons-home-modern',
color: 'lime',
to: '#warehouses'
},
{
label: 'Inventory',
icon: 'i-heroicons-gift-top',
color: 'yellow',
to: '#inventory'
},
]
</script>
28 changes: 28 additions & 0 deletions docs/components/content/examples/ListExampleObjects.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<template>
<UList :items="items" />
</template>

<script setup>
const items = [
{
label: 'Store',
icon: 'i-heroicons-building-storefront'
},
{
label: 'Clients',
icon: 'i-heroicons-user-group'
},
{
label: 'Products',
icon: 'i-heroicons-gift'
},
{
label: 'Warehouses',
icon: 'i-heroicons-home-modern'
},
{
label: 'Inventory',
icon: 'i-heroicons-gift-top'
},
]
</script>
38 changes: 38 additions & 0 deletions docs/components/content/examples/ListExampleSlotDefault.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<template>
<UList>
<div v-for="item in items" :key="item.label" class="flex flex-row gap-4 flex-nowrap items-center justify-between">
<div class="grow flex items-center gap-2">
<UIcon :name="item.icon" />
{{ item.label }}
</div>
<div class="font-mono text-gray-500">
{{ item.count.toLocaleString() }}
</div>
</div>
</UList>
</template>

<script setup>
const items = [
{
label: 'Phones',
icon: 'i-heroicons-device-phone-mobile',
count: 17346
},
{
label: 'Tablets',
icon: 'i-heroicons-device-tablet',
count: 6349
},
{
label: 'Laptops & desktops',
icon: 'i-heroicons-computer-desktop',
count: 1647
},
{
label: 'Servers',
icon: 'i-heroicons-server-stack',
count: 574
},
]
</script>
12 changes: 12 additions & 0 deletions docs/components/content/examples/ListExampleSlotSeparator.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<template>
<UList :items="items" orientation="horizontal">
<template #separator="{ isLast }">
<UIcon v-if="isLast" name="i-heroicons-arrow-right-circle" class="mx-2 h-6 w-6 text-primary-500" />
<UIcon v-else name="i-heroicons-arrow-small-right" class="mx-2 text-gray-500"/>
</template>
</Ulist>
</template>

<script setup>
const items = ['Home', 'Account', 'Security', 'Password Reset']
</script>
11 changes: 11 additions & 0 deletions docs/components/content/examples/ListExampleWrap.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<template>
<UList :items="items" class="w-56" orientation="horizontal" :separator="false" :wrap="true">
<div v-for="item in items" :key="item" class="grow rounded-full ring-1 ring-gray-300 dark:ring-gray-700 py-0.5 px-2 m-2 text-sm">
{{ item }}
</div>
</UList>
</template>

<script setup>
const items = ['First', 'Second', 'Third', 'Password Reset']
</script>
219 changes: 219 additions & 0 deletions docs/content/4.data/2.list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
---
description: Create horizontal o vertical lists with separators.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/layout/List.vue
---

## Usage

The `UList` component is a simple but flexible component to list items. The most basic approach is to set a list of items as an array of strings.

::component-card
---
componentClass: 'w-full lg:w-56'
baseProps:
items:
- Store
- Clients
- Products
- Warehouses
- Inventory
---
::

A more complex but flexible approach is to use a list of objects with the following properties:

- `label` - The text to show for the item
- `icon` - The icon to add before the item label
- `trailing` - If the icon should be after the item label

:component-example{component="list-example-objects" componentClass="w-full lg:w-56"}

If you set the `to` or `click` properties of the item, it will be transformed into a [Button](/elements/button). All props of a button will be passed to the Button component. The `variant` of the Button is set to `link` unless set otherwise.

:component-example{component="list-example-object-buttons" componentClass="w-full lg:w-56"}

Additionally, you can have total control on each item by using the [component slots](#slots).

::callout{icon="i-heroicons-light-bulb"}
Lists are unordered by default, and are created using the `ul` tag. You can change the tag to `ol` for semantically ordered list by setting the `ordered` prop to `true`. This does not make any impact on the List style.
::

### Separator

The visual separation of each item is done through the separator, which by default is a line done with a `div` element. It is oriented depending on the list orientation.

You can disable the separator using `separator` with the `false` value, which will bring each item together.

::component-card
---
componentClass: 'w-full lg:w-56'
baseProps:
items:
- Store
- Clients
- Products
- Warehouses
- Inventory
props:
separator: false
---
::

Alternatively, you may use a custom separator with the [`separator`](#separator-1) slot.

### Separator Style

The separator color can be changed using the `separatorColor`.

Besides all the colors from the `ui.colors` object, you can also use the `white`, `gray` and `black` colors with their pre-defined variants.

::component-card
---
componentClass: 'w-full lg:w-56'
baseProps:
items:
- Store
- Clients
- Products
- Warehouses
- Inventory
props:
separatorColor: primary
---
::

### Separator Trailing

The separator is created at the start of each item, from the second item in the list, which organically makes the separator absent when the list contains only one item.

By setting the `trailing` property to `true`, it makes the separator appear on all items except the last one. The list will still remove the separator if the list contains only one item.

Most of the time you won't need to change where the separator is located, but the option is available if you plan to make deep visual changes.

::component-card
---
componentClass: 'w-full lg:w-56'
baseProps:
items:
- Store
- Clients
- Products
- Warehouses
- Inventory
props:
separatorTrailing: true
---
::

### Orientation

By default, items are stacked vertically. To stack the list items horizontally, set the `orientation` prop to `horizontal`.

::component-card
---
extraClass: "overflow-x-auto"
baseProps:
items:
- Store
- Clients
- Products
- Warehouses
- Inventory
props:
orientation: 'horizontal'
options:
- name: orientation
restriction: only
values:
- 'horizontal'
- 'vertical'
---
::

::callout{icon="i-heroicons-light-bulb"}
You can also change the default orientation for all list through the [global UI config](/getting-started/theming#appconfigts).
::

### Wrapping

When the items exceed the container height or width, these will not be wrapped into another line. To enable this behaviour, set the `wrap` prop.

:component-example{component="list-example-wrap"}

### Ordered

By default, the list is rendered using the `ul` element. To render it as a semantically ordered list as `ol`, set the `ordered` prop.

::code-group

```vue [.vue]
<template>
<UList :items="items" ordered />
</template>
<script setup>
const items = [ 'Store', 'Clients', 'Products', 'Warehouses', 'Inventory' ]
</script>
```

```html [.html]
<ol class="...">
<li class="...">Store</li>
<li class="...">Clients</li>
<li class="...">Products</li>
<li class="...">Warehouses</li>
<li class="...">Inventory</li>
</ol>
```
::

### Key

Items are _keyed_ by their object. This may work for animation libraries or other utilities that rely on object comparison, but may come at some performance in some scenarios.

You may set a property value as key name using the `by` prop. It accepts `dot.notation` to access to deep values. When the key it's not found, it uses the item's array position by default.

```vue
<template>
<UList :items="items" key="label" />
</template>
<script setup>
const items = [
{ label: 'Store', icon: 'i-heroicons-building-storefront' },
{ label: 'Clients', icon: 'i-heroicons-user-group' },
{ label: 'Products', icon: 'i-heroicons-gift' },
{ label: 'Warehouses', icon: 'i-heroicons-home-modern' },
{ label: 'Inventory', icon: 'i-heroicons-gift-top' },
]
</script>
```

## Slots

### `default`

You can use the default slot to add your own set of items. You can use plain HTML elements, components, or a loop of components. Each child will be moved wrapped into its respective `<li>` element.

:component-example{component="list-example-slot-default"}

### `separator`

Use this slot to set a separator. It receives the current `index` of the item where is located, and both `isFirst` and `isLast` boolean if is the first or last item of the list, respectively.

:component-example{component="list-example-slot-separator" extraClass="overflow-x-auto"}

::callout{icon="i-heroicons-exclamation-triangle"}
Both `isFirst` and `isLast` booleans always returns `true` if there is only one item.
::

## Props

:component-props

## Config

:component-preset
Loading

0 comments on commit 429c852

Please sign in to comment.