-
Notifications
You must be signed in to change notification settings - Fork 594
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9b976a0
commit ac5e90b
Showing
12 changed files
with
389 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<template> | ||
<UList> | ||
<div v-for="(item, key) in items" :key="key" class="flex items-center gap-2"> | ||
<UIcon :name="item.icon" /> {{ item.label }} | ||
</div> | ||
|
||
<template #separator-after> | ||
<hr class="border-gray-200 dark:border-gray-800 my-2"> | ||
</template> | ||
</UList> | ||
</template> | ||
|
||
<script setup> | ||
const items = [ | ||
{ label: 'Home', icon: 'i-heroicons-home' }, | ||
{ label: 'Profile', icon: 'i-heroicons-user-circle' }, | ||
{ label: 'Security', icon: 'i-heroicons-shield-check' }, | ||
{ label: 'Password Reset', icon: 'i-heroicons-key' } | ||
] | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<template> | ||
<UList gap> | ||
<div v-for="(item, key) in items" :key="key" class="rounded bg-gray-200 dark:bg-gray-800 px-2 flex items-center gap-2"> | ||
<UIcon :name="item.icon" /> {{ item.label }} | ||
</div> | ||
</UList> | ||
</template> | ||
|
||
<script setup> | ||
const items = [ | ||
{ label: 'Home', icon: 'i-heroicons-home' }, | ||
{ label: 'Users', icon: 'i-heroicons-users' }, | ||
{ label: 'Carts', icon: 'i-heroicons-shopping-bag' }, | ||
{ label: 'Shipments', icon: 'i-heroicons-truck' } | ||
] | ||
</script> |
25 changes: 25 additions & 0 deletions
25
docs/components/content/examples/ListExampleItemOrientation.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<template> | ||
<UList item-orientation="horizontal"> | ||
<template #separator-before="{ index }"> | ||
<div | ||
class="flex items-center text-right text-green-500 pr-2" | ||
:style="`padding-left: ${Math.max(0, index - 1)}em`" | ||
> | ||
<UIcon name="i-heroicons-arrow-right" /> | ||
</div> | ||
</template> | ||
|
||
<div v-for="(item, key) in items" :key="key" class="flex items-center gap-2"> | ||
{{ item.label }} | ||
</div> | ||
</UList> | ||
</template> | ||
|
||
<script setup> | ||
const items = [ | ||
{ label: 'Home' }, | ||
{ label: 'Profile' }, | ||
{ label: 'Security' }, | ||
{ label: 'Password Reset' } | ||
] | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<template> | ||
<UList :ordered="true"> | ||
<div v-for="(item, key) in items" :key="key" class="flex items-center gap-2"> | ||
<UIcon :name="item.icon" /> {{ item.label }} | ||
</div> | ||
|
||
<template #separator-after> | ||
<hr class="border-gray-200 dark:border-gray-800 my-2" /> | ||
</template> | ||
</UList> | ||
</template> | ||
|
||
<script setup> | ||
const items = [ | ||
{ label: 'Home', icon: 'i-heroicons-home' }, | ||
{ label: 'Profile', icon: 'i-heroicons-user-circle' }, | ||
{ label: 'Security', icon: 'i-heroicons-shield-check' }, | ||
{ label: 'Password Reset', icon: 'i-heroicons-key' } | ||
] | ||
</script> |
22 changes: 22 additions & 0 deletions
22
docs/components/content/examples/ListExampleOrientation.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<template> | ||
<UList orientation="horizontal"> | ||
<div v-for="(item, key) in items" :key="key" class="flex items-center gap-2"> | ||
{{ item.label }} | ||
</div> | ||
|
||
<template #separator-after> | ||
<div class="flex items-center px-1 text-gray-400 dark:text-gray-700"> | ||
<UIcon name="i-heroicons-chevron-right" /> | ||
</div> | ||
</template> | ||
</UList> | ||
</template> | ||
|
||
<script setup> | ||
const items = [ | ||
{ label: 'Home' }, | ||
{ label: 'Profile' }, | ||
{ label: 'Security' }, | ||
{ label: 'Password Reset' } | ||
] | ||
</script> |
16 changes: 16 additions & 0 deletions
16
docs/components/content/examples/ListExamplePaddingClass.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<template> | ||
<UList class="gap-y-1"> | ||
<div v-for="(item, key) in items" :key="key" class="rounded bg-gray-200 dark:bg-gray-800 px-2 flex items-center gap-2"> | ||
<UIcon :name="item.icon" /> {{ item.label }} | ||
</div> | ||
</UList> | ||
</template> | ||
|
||
<script setup> | ||
const items = [ | ||
{ label: 'Home', icon: 'i-heroicons-home' }, | ||
{ label: 'Users', icon: 'i-heroicons-users' }, | ||
{ label: 'Carts', icon: 'i-heroicons-shopping-bag' }, | ||
{ label: 'Shipments', icon: 'i-heroicons-truck' } | ||
] | ||
</script> |
20 changes: 20 additions & 0 deletions
20
docs/components/content/examples/ListExampleSlotSeparatorAfter.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<template> | ||
<UList> | ||
<div v-for="(item, key) in items" :key="key" class="flex items-center gap-2"> | ||
<UIcon :name="item.icon" /> {{ item.label }} | ||
</div> | ||
|
||
<template #separator-after="{ index, isFirst, isLast }"> | ||
Check failure on line 7 in docs/components/content/examples/ListExampleSlotSeparatorAfter.vue GitHub Actions / ci (ubuntu-latest, 18)
Check failure on line 7 in docs/components/content/examples/ListExampleSlotSeparatorAfter.vue GitHub Actions / ci (ubuntu-latest, 18)
|
||
<hr class="border-gray-200 dark:border-gray-800 my-2"> | ||
</template> | ||
</UList> | ||
</template> | ||
|
||
<script setup> | ||
const items = [ | ||
{ label: 'Home', icon: 'i-heroicons-home' }, | ||
{ label: 'Profile', icon: 'i-heroicons-user-circle' }, | ||
{ label: 'Security', icon: 'i-heroicons-shield-check' }, | ||
{ label: 'Password Reset', icon: 'i-heroicons-key' } | ||
] | ||
</script> |
20 changes: 20 additions & 0 deletions
20
docs/components/content/examples/ListExampleSlotSeparatorBefore.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<template> | ||
<UList> | ||
<template #separator-before="{ index, isFirst, isLast }"> | ||
Check failure on line 3 in docs/components/content/examples/ListExampleSlotSeparatorBefore.vue GitHub Actions / ci (ubuntu-latest, 18)
Check failure on line 3 in docs/components/content/examples/ListExampleSlotSeparatorBefore.vue GitHub Actions / ci (ubuntu-latest, 18)
|
||
<hr class="border-gray-200 dark:border-gray-800 my-2"> | ||
</template> | ||
|
||
<div v-for="(item, key) in items" :key="key" class="flex items-center gap-2"> | ||
<UIcon :name="item.icon" /> {{ item.label }} | ||
</div> | ||
</UList> | ||
</template> | ||
|
||
<script setup> | ||
const items = [ | ||
{ label: 'Home', icon: 'i-heroicons-home' }, | ||
{ label: 'Profile', icon: 'i-heroicons-user-circle' }, | ||
{ label: 'Security', icon: 'i-heroicons-shield-check' }, | ||
{ label: 'Password Reset', icon: 'i-heroicons-key' } | ||
] | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<template> | ||
<div class="w-64"> | ||
<UList wrap orientation="horizontal" gap> | ||
<div | ||
v-for="(item, key) in items" | ||
:key="key" | ||
class="w-30 rounded bg-gray-200 dark:bg-gray-800 px-2 flex items-center gap-2" | ||
> | ||
<UIcon :name="item.icon" /> {{ item.label }} | ||
</div> | ||
</UList> | ||
</div> | ||
</template> | ||
|
||
<script setup> | ||
const items = [ | ||
{ label: 'Home', icon: 'i-heroicons-home' }, | ||
{ label: 'Users', icon: 'i-heroicons-users' }, | ||
{ label: 'Carts', icon: 'i-heroicons-shopping-bag' }, | ||
{ label: 'Shipments', icon: 'i-heroicons-truck' } | ||
] | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
--- | ||
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 | ||
|
||
Add list items to the `UList` component, and a separator using the [`separator-before`](#separator-before) or [`separator-after`](#separator-after) slots. | ||
|
||
List items can be from simple HTML elements, to a `v-for` loop of components. | ||
|
||
:component-example{component="list-example"} | ||
|
||
Lists are unordered by default, created using the `ul` tag. You may change the HTML element to `ol`, which semantically makes the list _ordered_, by setting the `ordered` prop to `true`. | ||
|
||
:component-example{component="list-example-ordered"} | ||
|
||
::callout{icon="i-heroicons-light-bulb"} | ||
Changing `ordered` to `true` makes only a semantic change, not a visual change. | ||
:: | ||
|
||
### Orientation | ||
|
||
By default, items are stacked vertically. To stack the items horizontally, set the `orientation` prop to `horizontal`. | ||
|
||
:component-example{component="list-example-orientation"} | ||
|
||
### Item Orientation | ||
|
||
Both list and separators have the same stacking orientation. By default, as the list is vertical, and each item (contents and separators) are also stacked vertically. | ||
|
||
The `itemOrientation` allows to change the stacking orientation of the items regardless of the list orientation. | ||
|
||
:component-example{component="list-example-item-orientation"} | ||
|
||
### Gap | ||
|
||
To add a default space in between each item, use the `gap` prop as `true`. | ||
|
||
:component-example{component="list-example-gap"} | ||
|
||
Alternatively, you may add a gap manually using the `class` attribute like any other HTML Element. | ||
|
||
:component-example{component="list-example-padding-class"} | ||
|
||
::callout{icon="i-heroicons-light-bulb"} | ||
Items are listed using Tailwind CSS [`flex`](https://tailwindcss.com/docs/flex). You can change this through the [UI configuration](#config). | ||
:: | ||
|
||
### 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 to `true`. | ||
|
||
:component-example{component="list-example-wrap"} | ||
|
||
## Slots | ||
|
||
### `separator-before` | ||
|
||
Use this slot to set a separator **before** the item **contents**. 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-before"} | ||
|
||
::callout{icon="i-heroicons-exclamation-triangle"} | ||
Both `isFirst` and `isLast` booleans always returns `true` if there is only one item. | ||
:: | ||
|
||
### `separator-after` | ||
|
||
Use this slot to set a separator **after** the item **contents**. 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-after"} | ||
|
||
::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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import { h, computed, toRef, defineComponent, onMounted } from 'vue' | ||
import type { PropType, SlotsType } from 'vue' | ||
import type { RequireAtLeastOne } from 'type-fest' | ||
import { twMerge, twJoin } from 'tailwind-merge' | ||
import { useUI } from '../../composables/useUI' | ||
import type { Strategy } from '../../types' | ||
// @ts-expect-error | ||
import appConfig from '#build/app.config' | ||
import { mergeConfig, getSlotsChildren } from '#ui/utils' | ||
import { list } from '#ui/ui.config' | ||
|
||
const config = mergeConfig<typeof list>(appConfig.ui.strategy, appConfig.ui.list, list) | ||
|
||
export default defineComponent({ | ||
inheritAttrs: false, | ||
props: { | ||
ordered: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
orientation: { | ||
type: String as PropType<'horizontal' | 'vertical'>, | ||
default: 'vertical', | ||
validator (value: string) { | ||
return Object.keys(config).includes(value) | ||
} | ||
}, | ||
gap: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
wrap: { | ||
type: Boolean, | ||
default: () => config.wrapItems | ||
}, | ||
itemOrientation: { | ||
type: String as PropType<'horizontal' | 'vertical'>, | ||
default: undefined, | ||
validator (value: string|undefined) { | ||
return typeof value === 'string' ? Object.keys(config).includes(value) : true | ||
} | ||
}, | ||
class: { | ||
type: [String, Object, Array] as PropType<any>, | ||
default: undefined | ||
}, | ||
ui: { | ||
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>, | ||
default: undefined | ||
} | ||
}, | ||
slots: Object as SlotsType< | ||
RequireAtLeastOne<{ | ||
default: undefined, | ||
'separator-before'?: { index: number, isFirst: boolean, isLast: boolean }, | ||
'separator-after'?: { index: number, isFirst: boolean, isLast: boolean }, | ||
}, 'separator-before' | 'separator-after'> | ||
>, | ||
setup (props, { slots }) { | ||
const { ui, attrs } = useUI('list', toRef(props, 'ui'), config) | ||
|
||
const listClass = computed(() => { | ||
return twMerge(twJoin( | ||
ui.value[props.orientation].base, | ||
props.wrap ? ui.value.wrap : ui.value.nowrap, | ||
props.gap ? ui.value[props.orientation].gap : '' | ||
), props.class) | ||
}) | ||
|
||
const itemClass = computed(() => { | ||
return twJoin( | ||
ui.value[props.itemOrientation ?? props.orientation].base, | ||
ui.value.nowrap | ||
) | ||
}) | ||
|
||
function addSeparator (array) { | ||
return array.map((item, index) => { | ||
const children = [] | ||
|
||
const isFirst = array.length === 1 | ||
|| ((slots['separator-before'] && index === 1) || (slots['separator-after'] && index === 0)) | ||
|
||
const isLast = array.length === 1 | ||
|| ((slots['separator-before'] && index === (array.length - 1)) || (slots['separator-after'] && index === (array.length - 2))) | ||
|
||
if (slots['separator-before'] && (index > 0)) { | ||
children.push(slots['separator-before']({ index, isFirst, isLast })) | ||
} | ||
|
||
children.push(item) | ||
|
||
if (slots['separator-after'] && (index < (array.length - 1))) { | ||
children.push(slots['separator-after']({ index, isFirst, isLast })) | ||
} | ||
|
||
return h('li', { class: itemClass.value }, children) | ||
}) | ||
} | ||
|
||
const children = computed(() => addSeparator(getSlotsChildren(slots))) | ||
|
||
const orderedElement = computed(() => props.ordered ? 'ol' : 'ul') | ||
|
||
return () => h(orderedElement.value, { ...attrs, class: listClass.value, ref: 'list' }, children.value) | ||
} | ||
}) |
Oops, something went wrong.