Skip to content

Commit

Permalink
feat(table): add loading state (#259)
Browse files Browse the repository at this point in the history
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
  • Loading branch information
Haythamasalama and benjamincanac committed Jun 12, 2023
1 parent d20983d commit 4741532
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 6 deletions.
86 changes: 86 additions & 0 deletions docs/components/content/examples/TableExampleLoadingSlot.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<script setup>
const columns = [{
key: 'name',
label: 'Name'
}, {
key: 'title',
label: 'Title'
}, {
key: 'email',
label: 'Email'
}, {
key: 'role',
label: 'Role'
}, {
key: 'actions'
}]
const people = []
const pending = ref(true)
</script>

<template>
<UTable :rows="people" :columns="columns" :loading="pending">
<template #loading-state>
<div class="flex items-center justify-center h-32">
<i class="loader --6" />
</div>
</template>
</UTable>
</template>

<style scoped>
/* https://codepen.io/jenning/pen/YzNmzaV */
.loader {
--color: rgb(var(--color-primary-400));
--size-mid: 6vmin;
--size-dot: 1.5vmin;
--size-bar: 0.4vmin;
--size-square: 3vmin;
display: block;
position: relative;
width: 50%;
display: grid;
place-items: center;
}
.loader::before,
.loader::after {
content: '';
box-sizing: border-box;
position: absolute;
}
/**
loader --6
**/
.loader.--6::before {
width: var(--size-square);
height: var(--size-square);
background-color: var(--color);
top: calc(50% - var(--size-square));
left: calc(50% - var(--size-square));
animation: loader-6 2.4s cubic-bezier(0, 0, 0.24, 1.21) infinite;
}
@keyframes loader-6 {
0%, 100% {
transform: none;
}
25% {
transform: translateX(100%);
}
50% {
transform: translateX(100%) translateY(100%);
}
75% {
transform: translateY(100%);
}
}
</style>
94 changes: 91 additions & 3 deletions docs/content/4.data/1.table.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,11 +401,60 @@ const rows = computed(() => {
```
::

### Loading :u-badge{label="Edge" class="ml-2 align-text-bottom !rounded-full"}

Use the `loading` prop to display a loading state.

Use the `loading-state` prop to customize the `icon` and `label` or change them globally in `ui.table.default.loadingState`.

You can also set it to `null` to hide the loading state.

::component-card
---
padding: false
overflowClass: 'overflow-x-auto'
baseProps:
class: 'w-full'
columns:
- key: 'id'
label: 'ID'
- key: 'name'
label: 'Name'
- key: 'title'
label: 'Title'
- key: 'email'
label: 'Email'
- key: 'role'
label: 'Role'
props:
loading: true
loadingState:
icon: 'i-heroicons-arrow-path-20-solid'
label: "Loading..."
excludedProps:
- loadingState
---
::

This can be easily used with Nuxt `useAsyncData` composable.

```vue
<script setup>
const columns = [...]
const { pending, data: people } = await useLazyAsyncData('people', () => $fetch('/api/people'))
</script>
<template>
<UTable :rows="people" :columns="columns" :loading="pending" />
</template>
```

### Empty

Use the `empty-state` prop to display a message when there are no results.
An empty state will be displayed when there are no results.

You can pass an `object` through the `empty-state` prop or globally through `ui.table.default.emptyState`.
Use the `empty-state` prop to customize the `icon` and `label` or change them globally in `ui.table.default.emptyState`.

You can also set it to `null` to hide the empty state.

Expand Down Expand Up @@ -517,7 +566,46 @@ const selected = ref([people[1]])
```
::

### `empty-state` :u-badge{label="Edge" class="ml-2 align-text-bottom"}
### `loading-state` :u-badge{label="Edge" class="ml-2 align-text-bottom !rounded-full"}

Use the `#loading-state` slot to customize the loading state.

::component-example{class="grid"}
---
padding: false
overflowClass: 'overflow-x-auto'
---

#default
:table-example-loading-slot{class="flex-1"}

#code
```vue
<script setup>
const columns = [...]
const people = []
const pending = ref(true)
</script>
<template>
<UTable :rows="people" :columns="columns" :loading="pending">
<template #loading-state>
<div class="flex items-center justify-center h-32">
<i class="loader --6" />
</div>
</template>
</UTable>
</template>
<style scoped>
/* https://codepen.io/jenning/pen/YzNmzaV */
</style>
```
::

### `empty-state` :u-badge{label="Edge" class="ml-2 align-text-bottom !rounded-full"}

Use the `#empty-state` slot to customize the empty state.

Expand Down
4 changes: 2 additions & 2 deletions docs/content/5.navigation/2.command-palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,9 @@ excludedProps:

### Empty

Use the `empty-state` prop to display a message when there are no results.
An empty state will be displayed when there are no results.

You can pass an `object` through the `empty-state` prop or globally through `ui.commandPalette.default.emptyState`.
Use the `empty-state` prop to customize the `icon` and `label` or change them globally in `ui.commandPalette.default.emptyState`.

You can also set it to `null` to hide the empty state.

Expand Down
9 changes: 9 additions & 0 deletions src/runtime/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ const table = {
font: '',
size: 'text-sm'
},
loadingState: {
wrapper: 'flex flex-col items-center justify-center flex-1 px-6 py-14 sm:px-14',
label: 'text-sm text-center text-gray-900 dark:text-white',
icon: 'w-6 h-6 mx-auto text-gray-400 dark:text-gray-500 mb-4 animate-spin'
},
emptyState: {
wrapper: 'flex flex-col items-center justify-center flex-1 px-6 py-14 sm:px-14',
label: 'text-sm text-center text-gray-900 dark:text-white',
Expand All @@ -40,6 +45,10 @@ const table = {
variant: 'ghost',
class: '-m-1.5'
},
loadingState: {
icon: 'i-heroicons-arrow-path-20-solid',
label: 'Loading...'
},
emptyState: {
icon: 'i-heroicons-circle-stack-20-solid',
label: 'No items.'
Expand Down
23 changes: 22 additions & 1 deletion src/runtime/components/data/Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,20 @@
</td>
</tr>

<tr v-if="emptyState && !rows.length">
<tr v-if="loadingState && loading">
<td :colspan="columns.length">
<slot name="loading-state">
<div :class="ui.loadingState.wrapper">
<UIcon v-if="loadingState.icon" :name="loadingState.icon" :class="ui.loadingState.icon" aria-hidden="true" />
<p :class="ui.loadingState.label">
{{ loadingState.label }}
</p>
</div>
</slot>
</td>
</tr>

<tr v-else-if="emptyState && !rows.length">
<td :colspan="columns.length">
<slot name="empty-state">
<div :class="ui.emptyState.wrapper">
Expand Down Expand Up @@ -106,6 +119,14 @@ export default defineComponent({
type: String,
default: () => appConfig.ui.table.default.sortDescIcon
},
loading: {
type: Boolean,
default: false
},
loadingState: {
type: Object as PropType<{ icon: string, label: string }>,
default: () => appConfig.ui.table.default.loadingState
},
emptyState: {
type: Object as PropType<{ icon: string, label: string }>,
default: () => appConfig.ui.table.default.emptyState
Expand Down

1 comment on commit 4741532

@vercel
Copy link

@vercel vercel bot commented on 4741532 Jun 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

ui – ./

ui-nuxtlabs.vercel.app
ui-git-dev-nuxtlabs.vercel.app
ui.nuxtlabs.com

Please sign in to comment.